diff --git a/.dir-locals.el b/.dir-locals.el new file mode 100644 index 000000000..484dee737 --- /dev/null +++ b/.dir-locals.el @@ -0,0 +1,5 @@ +;;; Directory Local Variables +;;; For more information see (info "(emacs) Directory Variables") + +((nil + (indent-tabs-mode))) diff --git a/.gitignore b/.gitignore index f90d75d56..e91237ad7 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,5 @@ tunnel.log /well-known /.well-known-cache /**/tmp-* +/testdata/bigAlloc_*.csv +/sessions \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b955152b5..9e7b3bb7b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -203,7 +203,6 @@ hlint: - stack test --flag uniworx:-library-only --flag uniworx:-dev --flag uniworx:pedantic uniworx:test:hlint needs: - frontend:build - - yesod:build # For caching before_script: - apt-get update -y - apt-get install -y --no-install-recommends locales-all @@ -227,7 +226,6 @@ yesod:test: - stack test --coverage --flag uniworx:-library-only --flag uniworx:-dev --flag uniworx:pedantic --skip hlint needs: - frontend:build - - yesod:build # For caching before_script: - apt-get update -y - apt-get install -y --no-install-recommends locales-all @@ -249,6 +247,7 @@ deploy:uniworx3: - yesod:build - yesod:test # For sanity - hlint # For sanity + - frontend:test # For sanity before_script: - apt-get update -y - apt-get install -y --no-install-recommends openssh-client diff --git a/CHANGELOG.md b/CHANGELOG.md index 0571e323b..e8419ac1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,393 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [16.0.5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v16.0.4...v16.0.5) (2020-05-06) + + +### Bug Fixes + +* **migration:** handle deleted courses & users ([35621df](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/35621df)) + + + +### [16.0.4](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v16.0.3...v16.0.4) (2020-05-06) + + +### Bug Fixes + +* **migration:** typos ([e508277](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e508277)) + + + +### [16.0.3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v16.0.2...v16.0.3) (2020-05-05) + + +### Bug Fixes + +* **i18n:** s/Typ/Art/ ([0e43851](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0e43851)), closes [#493](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/493) +* **migration:** typo ([fb7c7ef](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fb7c7ef)) + + + +### [16.0.2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v16.0.1...v16.0.2) (2020-05-05) + + +### Bug Fixes + +* **corrections-grade-r:** add get following post ([14f9ab6](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/14f9ab6)), closes [#532](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/532) +* **jobs:** reduce likelihood for multiple queueing of notifications ([970ca78](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/970ca78)) + + + +### [16.0.1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v16.0.0...v16.0.1) (2020-05-05) + + +### Bug Fixes + +* **exams:** don't show manual bonus as inconsistent ([fb54c84](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fb54c84)) +* **interactive-fieldset:** fix behaviour for nested fieldsets ([65b429a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/65b429a)) + + + +## [16.0.0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v15.6.1...v16.0.0) (2020-05-05) + + +### Features + +* **async-table:** history api ([c348b7c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c348b7c)), closes [#426](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/426) +* **course-participants:** course-deregister-no-show ([bf64eaf](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/bf64eaf)), closes [#499](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/499) +* **course-participants:** introduce CourseParticipantState ([d5b65a1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d5b65a1)), closes [#499](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/499) [#371](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/371) +* **generic-file-field:** prevent multiple session files of same name ([98e1141](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/98e1141)) +* **http-client:** baseUrl and defaultUrl ([693189f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/693189f)) +* **i18n:** missing translations ([153bb1f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/153bb1f)) + + +### BREAKING CHANGES + +* **course-participants:** CourseParticipantState + + + +### [15.6.1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v15.6.0...v15.6.1) (2020-04-30) + + +### Bug Fixes + +* **submission-groups:** prevent deleting group before insert ([f87cf7a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f87cf7a)) + + + +## [15.6.0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v15.5.0...v15.6.0) (2020-04-28) + + +### Bug Fixes + +* **health:** ldap check only admins ([f889ec6](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f889ec6)) +* **i18n:** submissionDownloadAnonymous ([e6af788](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e6af788)) +* typo ([52670bc](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/52670bc)) + + +### Features + +* **corrections:** non-anonymous download w/ registered groups ([9032f80](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9032f80)) +* **sheets:** submission groups & rework sheet form ([57f1ce9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/57f1ce9)) +* **submission-groups:** invite w/ submission-group & audit ([7f10d44](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7f10d44)) + + + +## [15.5.0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v15.4.1...v15.5.0) (2020-04-27) + + +### Bug Fixes + +* **auth:** tutors may see sheet list ([e0c05f3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e0c05f3)) +* **campus:** fix corner case with study features ([76098cc](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/76098cc)) + + +### Features + +* **allocations:** switch to csprng ([3ea7371](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3ea7371)) +* **ldap:** failover ([0e68b6c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0e68b6c)) +* **news:** timeout sheets after a month ([31aa25a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/31aa25a)) + + + +### [15.4.1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v15.4.0...v15.4.1) (2020-04-26) + + +### Bug Fixes + +* **allocation:** don't restart cloneCount when allocating successors ([e1c6fd4](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e1c6fd4)) + + + +## [15.4.0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v15.3.0...v15.4.0) (2020-04-24) + + +### Bug Fixes + +* typo ([c06a472](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c06a472)) +* **faqs:** mention mail to set password ([32097d1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/32097d1)) +* **faqs:** wording ([02d284f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/02d284f)) +* **navbar:** restore border to language buttons ([a2e9a9c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a2e9a9c)) + + +### Features + +* **faqs:** i18n ([a1a0fa3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a1a0fa3)) +* **faqs:** initial ([7b53377](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7b53377)) +* **faqs:** more faqs ([18766ed](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/18766ed)) +* **faqs:** more links to faq ([10d44d1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/10d44d1)) +* **help:** attach last error message ([fdd6b1a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fdd6b1a)) + + + +## [15.3.0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v15.2.0...v15.3.0) (2020-04-23) + + +### Bug Fixes + +* **memcached:** navAccess & quick actions cache invalidations ([d05306a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d05306a)) +* **system-message:** lastChanged & unhide logic error ([36abb3e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/36abb3e)) + + +### Features + +* **robots.txt:** disallow ahrefs ([9afee89](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9afee89)) + + + +## [15.2.0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v15.1.2...v15.2.0) (2020-04-22) + + +### Bug Fixes + +* **health:** more generous healthchecks ([466203d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/466203d)) + + +### Features + +* **caching:** aggressively cache nav items ([b9b0909](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b9b0909)) +* **memcached:** introduce general purpose memcached ([e8c2dc5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e8c2dc5)) + + + +### [15.1.2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v15.1.1...v15.1.2) (2020-04-19) + + +### Bug Fixes + +* **mass-input:** defaultValue is safe ([03f36ae](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/03f36ae)) + + + +### [15.1.1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v15.1.0...v15.1.1) (2020-04-17) + + +### Bug Fixes + +* **course-users:** deregistration w/ allocation & w/o reason ([4f237e1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/4f237e1)) + + + +## [15.1.0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v15.0.0...v15.1.0) (2020-04-17) + + +### Bug Fixes + +* **style:** padding of language buttons ([e704b23](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e704b23)) +* **tests:** fix build ([b0f2304](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b0f2304)) + + +### Features + +* **course-user:** authorisation checks ([d15792c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d15792c)) +* **course-user:** i18n ([da629a8](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/da629a8)) +* **course-user:** major improvements ([ced6ef2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ced6ef2)), closes [#126](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/126) +* **mass-input:** automatic add before submit ([7540a4f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7540a4f)) +* **submissions:** ignore additional filename components ([38f69c3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/38f69c3)) +* **submissions:** non-anonymized correction ([fd2c288](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fd2c288)), closes [#524](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/524) [#292](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/292) + + + +## [15.0.0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v14.6.0...v15.0.0) (2020-04-15) + + +### Bug Fixes + +* **allocations:** better handle participants without applications ([05d37fb](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/05d37fb)) +* bump changelog & translate ([a75f3eb](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a75f3eb)) + + +### Features + +* **system-messages:** hiding ([c81bc23](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c81bc23)) +* **system-messages:** refactor cookies & improve system messages ([ead6015](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ead6015)) + + +### BREAKING CHANGES + +* **system-messages:** names of cookies & configuration changed + + + +## [14.6.0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v14.5.0...v14.6.0) (2020-04-09) + + +### Bug Fixes + +* fix course duplicate message & name -> title for courses ([d87e8b7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d87e8b7)) +* hlint ([908e6de](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/908e6de)) + + +### Features + +* admin interface to issue tokens ([738ab7b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/738ab7b)) + + + +## [14.5.0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v14.4.0...v14.5.0) (2020-04-09) + + +### Features + +* **news:** show system messages ([0d39924](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0d39924)) +* **tokens:** multiple authorities ([bc47dcf](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/bc47dcf)) + + + +## [14.4.0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v14.1.1...v14.4.0) (2020-04-07) + + +### Bug Fixes + +* **dbtable:** improve sorting for haskell+sql ([fd8255d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fd8255d)) +* **exam-form:** allow finished without start ([fbc3680](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fbc3680)) +* **exams:** provide bonus information in return of examBonusGrade ([731231d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/731231d)) +* configure sessions to be strictly same-site ([a7e64bc](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a7e64bc)) +* **i18n:** add missing translations ([773c6c5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/773c6c5)) +* fix .dual-heated.degenerate ([6058692](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6058692)) + + +### Features + +* persist bearer tokens in session ([d8040e7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d8040e7)) +* **allocations:** compute & accept allocations ([20ef95c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/20ef95c)) +* **allocations:** display new allocations in user table ([bb20062](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/bb20062)) +* **allocations:** improve accept ui and logging ([3422fd7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3422fd7)) +* **allocations:** improve acceptance display ([cf03277](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cf03277)) +* **allocations:** improve display ([26f8f39](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/26f8f39)) +* **applications-list:** add warning regarding features of study ([cdbe12c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cdbe12c)) +* **course-events:** add HideColumns for course events ([1138f9e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1138f9e)) +* **course-events:** add optional note to course events ([6ad8f2e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6ad8f2e)) +* **course-events:** course event note text -> html ([c8904d1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c8904d1)) +* **course-events:** hide note column if there are no notes to display ([1ac7f4e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1ac7f4e)) +* **course-events:** show notes in course events table ([b2c4125](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b2c4125)) +* **exams:** convenience for automatic grade calculation ([ec6a8ae](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ec6a8ae)) +* **serversessions:** move session storage to dedicated memcached ([9960059](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9960059)), closes [#390](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/390) +* more date & time formats ([936c366](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/936c366)) + + + +## [14.3.0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v14.2.0...v14.3.0) (2020-03-31) + + +### Bug Fixes + +* **exam-form:** allow finished without start ([fbc3680](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fbc3680)) + + +### Features + +* **course-events:** add HideColumns for course events ([1138f9e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1138f9e)) +* **course-events:** add optional note to course events ([6ad8f2e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6ad8f2e)) +* **course-events:** course event note text -> html ([c8904d1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c8904d1)) +* **course-events:** hide note column if there are no notes to display ([1ac7f4e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1ac7f4e)) +* **course-events:** show notes in course events table ([b2c4125](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b2c4125)) + + + +## [14.2.0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v14.1.1...v14.2.0) (2020-03-22) + + +### Bug Fixes + +* **dbtable:** improve sorting for haskell+sql ([fd8255d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fd8255d)) +* **exams:** provide bonus information in return of examBonusGrade ([731231d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/731231d)) +* configure sessions to be strictly same-site ([a7e64bc](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a7e64bc)) +* **i18n:** add missing translations ([773c6c5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/773c6c5)) +* fix .dual-heated.degenerate ([6058692](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6058692)) + + +### Features + +* **allocations:** compute & accept allocations ([20ef95c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/20ef95c)) +* **allocations:** display new allocations in user table ([bb20062](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/bb20062)) +* **allocations:** improve accept ui and logging ([3422fd7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3422fd7)) +* **allocations:** improve acceptance display ([cf03277](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cf03277)) +* **allocations:** improve display ([26f8f39](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/26f8f39)) +* **applications-list:** add warning regarding features of study ([cdbe12c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cdbe12c)) +* **exams:** convenience for automatic grade calculation ([ec6a8ae](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ec6a8ae)) +* **serversessions:** move session storage to dedicated memcached ([9960059](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9960059)), closes [#390](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/390) +* more date & time formats ([936c366](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/936c366)) + + + +### [14.1.1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v14.1.0...v14.1.1) (2020-03-06) + + +### Bug Fixes + +* **csv-import:** major usability improvements ([2dc6641](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2dc6641)) + + + +## [14.1.0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v14.0.0...v14.1.0) (2020-03-06) + + +### Bug Fixes + +* fix build & minor refactor ([bb9b4f0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/bb9b4f0)) +* **course-users:** add missing dbt sorting ([1bc14c9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1bc14c9)) +* **course-users:** insertUnique and only count and audit true inserts ([1325ff2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1325ff2)) + + +### Features + +* **corrections:** submission filter ([38dbfe7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/38dbfe7)) +* **course-users:** allow for exam registration on CUsersR ([b8acc9b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b8acc9b)) +* **course-users:** exams in dbtable and csv ([c23becc](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c23becc)) +* **course-users:** filter by exam registrations ([1d7d0ab](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1d7d0ab)) +* **course-users:** match filter titles with column titles ([ecd7bec](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ecd7bec)) +* **course-users:** register exam action with optional occurrence ([34ad1df](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/34ad1df)) +* **csv:** export example data & improve zoned-time parsing ([49d9ab9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/49d9ab9)) + + + +## [14.0.0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v13.0.1...v14.0.0) (2020-03-03) + + +### Bug Fixes + +* **allocations:** show assignment green ([9d62b3a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9d62b3a)) + + +### Features + +* **allocations:** explanations & introduce grade-ordinal-proportion ([ee2e504](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ee2e504)) +* **allocations:** show & export priority ([7462e03](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7462e03)) +* **allocations:** table of allocation users ([2735d46](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2735d46)) +* **allocations:** tooltips listing courses in users table ([6bca64c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6bca64c)) +* **allocations:** upload of priorities ([a590f45](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a590f45)) + + +### BREAKING CHANGES + +* **allocations:** influence of grades on allocation priority now +relative when priorities are ordinal + + + ### [13.0.1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v13.0.0...v13.0.1) (2020-02-24) diff --git a/config/robots.txt b/config/robots.txt index 7d329b1db..155914d6c 100644 --- a/config/robots.txt +++ b/config/robots.txt @@ -1 +1,4 @@ User-agent: * + +User-agent: AhrefsBot +Disallow: / diff --git a/config/settings.yml b/config/settings.yml index 196f8e97e..47c517a15 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -5,7 +5,7 @@ static-dir: "_env:STATIC_DIR:static" well-known-dir: "_env:WELL_KNOWN_DIR:well-known" -well-known-link-file: "html_code.html" +well-known-link-file: html_code.html webpack-manifest: "_env:WEBPACK_MANIFEST:config/webpack.yml" host: "_env:HOST:*4" # any IPv4 host @@ -31,8 +31,8 @@ notification-rate-limit: 3600 notification-collate-delay: 7200 notification-expiration: 259200 session-timeout: 7200 -jwt-expiration: 604800 -jwt-encoding: HS256 +bearer-expiration: 604800 +bearer-encoding: HS256 maximum-content-length: "_env:MAX_UPLOAD_SIZE:134217728" session-files-expire: 3600 prune-unreferenced-files: 86400 @@ -67,9 +67,11 @@ ip-retention-time: 1209600 # Debugging auth-dummy-login: "_env:DUMMY_LOGIN:false" allow-deprecated: "_env:ALLOW_DEPRECATED:false" +encrypt-errors: "_env:ENCRYPT_ERRORS:true" +server-session-acid-fallback: "_env:SERVER_SESSION_ACID_FALLBACK:false" auth-pw-hash: - algorithm: "pbkdf2" + algorithm: pbkdf2 strength: 14 # Optional values with the following production defaults. @@ -77,7 +79,6 @@ auth-pw-hash: # reload-templates: false # mutable-static: false # skip-combining: false -# encrypt-errors: true database: user: "_env:PGUSER:uniworx" @@ -91,26 +92,28 @@ database: auto-db-migrate: '_env:AUTO_DB_MIGRATE:true' ldap: - host: "_env:LDAPHOST:" - tls: "_env:LDAPTLS:" - port: "_env:LDAPPORT:389" - user: "_env:LDAPUSER:" - pass: "_env:LDAPPASS:" - baseDN: "_env:LDAPBASE:" - scope: "_env:LDAPSCOPE:WholeSubtree" - timeout: "_env:LDAPTIMEOUT:5" - search-timeout: "_env:LDAPSEARCHTIME:5" - pool: - stripes: "_env:LDAPSTRIPES:1" - timeout: "_env:LDAPTIMEOUT:20" - limit: "_env:LDAPLIMIT:10" + - host: "_env:LDAPHOST:" + tls: "_env:LDAPTLS:" + port: "_env:LDAPPORT:389" + user: "_env:LDAPUSER:" + pass: "_env:LDAPPASS:" + baseDN: "_env:LDAPBASE:" + scope: "_env:LDAPSCOPE:WholeSubtree" + timeout: "_env:LDAPTIMEOUT:5" + search-timeout: "_env:LDAPSEARCHTIME:5" + pool: + stripes: "_env:LDAPSTRIPES:1" + timeout: "_env:LDAPTIMEOUT:20" + limit: "_env:LDAPLIMIT:10" + +ldap-re-test-failover: 60 smtp: host: "_env:SMTPHOST:" port: "_env:SMTPPORT:25" ssl: "_env:SMTPSSL:starttls" auth: - type: "login" + type: login user: "_env:SMTPUSER:" pass: "_env:SMTPPASS:" pool: @@ -119,20 +122,65 @@ smtp: limit: "_env:SMTPLIMIT:10" widget-memcached: - host: "_env:MEMCACHEDHOST:" - port: "_env:MEMCACHEDPORT:11211" + host: "_env:WIDGET_MEMCACHED_HOST:" + port: "_env:WIDGET_MEMCACHED_PORT:11211" auth: [] - limit: "_env:MEMCACHEDLIMIT:10" - timeout: "_env:MEMCACHEDTIMEOUT:20" - base-url: "_env:MEMCACHEDROOT:" - expiration: "_env:MEMCACHEDEXPIRATION:3600" + limit: "_env:WIDGET_MEMCACHED_LIMIT:1024" + timeout: "_env:WIDGET_MEMCACHED_TIMEOUT:20" + base-url: "_env:WIDGET_MEMCACHED_ROOT:" + expiration: "_env:WIDGET_MEMCACHED_EXPIRATION:3600" + +session-memcached: + host: "_env:SESSION_MEMCACHED_HOST:" + port: "_env:SESSION_MEMCACHED_PORT:11211" + auth: [] + limit: "_env:SESSION_MEMCACHED_LIMIT:1024" + timeout: "_env:SESSION_MEMCACHED_TIMEOUT:20" + expiration: "_env:SESSION_MEMCACHED_EXPIRATION:28807" + +memcached: + host: "_env:MEMCACHED_HOST:" + port: "_env:MEMCACHED_PORT:11211" + auth: [] + limit: "_env:MEMCACHED_LIMIT:1024" + timeout: "_env:MEMCACHED_TIMEOUT:20" + expiration: "_env:MEMCACHED_EXPIRATION:300" + +server-sessions: + idle-timeout: 28807 + absolute-timeout: 604801 + timeout-resolution: 601 + persistent-cookies: true +session-token-expiration: 28807 +session-token-encoding: HS256 + +cookies: + SESSION: + same-site: lax + http-only: true + secure: "_env:SERVER_SESSION_COOKIES_SECURE:true" + XSRF-TOKEN: + expires: null + same-site: strict + http-only: false + secure: "_env:COOKIES_SECURE:true" + LANG: + expires: 12622780800 + same-site: lax + http-only: false + secure: "_env:COOKIES_SECURE:true" + SYSTEM-MESSAGE-STATE: + expires: 12622780800 + same-site: lax + http-only: false + secure: "_env:COOKIES_SECURE:true" user-defaults: max-favourites: 12 max-favourite-terms: 2 theme: Default date-time-format: "%a %d %b %Y %R" - date-format: "%d.%m.%Y" + date-format: "%a %d %b %Y" time-format: "%R" download-files: false warning-days: 1209600 @@ -150,3 +198,9 @@ allocation-grade-ordinal-proportion: 0.075 instance-id: "_env:INSTANCE_ID:instance" ribbon: "_env:RIBBON:" + + +favourites-quick-actions-burstsize: 40 +favourites-quick-actions-avg-inverse-rate: 50e3 # µs/token +favourites-quick-actions-timeout: 40e-3 # s +favourites-quick-actions-cache-ttl: 120 # s diff --git a/config/test-settings.yml b/config/test-settings.yml index 5fb61bedf..7ba4552eb 100644 --- a/config/test-settings.yml +++ b/config/test-settings.yml @@ -4,9 +4,10 @@ database: log-settings: detailed: true all: true - minimum-level: "debug" - destination: "test.log" + minimum-level: debug + destination: test.log auth-dummy-login: true +server-session-acid-fallback: true job-workers: 1 diff --git a/db.sh b/db.sh index a66af88ba..afb0e1720 100755 --- a/db.sh +++ b/db.sh @@ -6,4 +6,6 @@ set -e [ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en .stack-work.lock "$0" "$@" || : stack build --fast --flag uniworx:-library-only --flag uniworx:dev + +export SERVER_SESSION_ACID_FALLBACK=${SERVER_SESSION_ACID_FALLBACK:-true} stack exec uniworxdb -- $@ diff --git a/frontend/src/app.sass b/frontend/src/app.sass index 51a8c4062..9e1d04814 100644 --- a/frontend/src/app.sass +++ b/frontend/src/app.sass @@ -236,17 +236,15 @@ h4 margin-top: 20px // GENERAL BUTTON STYLES -input[type="submit"], -input[type="button"], -button, -.btn +input[type="submit"]:not(.btn-link), +input[type="button"]:not(.btn-link), +button:not(.btn-link), +.btn:not(.btn-link) font-family: var(--font-base) outline: 0 - border: 0 box-shadow: 0 background-color: var(--color-dark) color: white - padding: 10px 17px min-width: 100px transition: all .1s font-size: 16px @@ -254,6 +252,10 @@ button, display: inline-block text-decoration: none + &:not(.navbar__container-link) + padding: 10px 17px + border: 0 + a:hover color: white @@ -264,41 +266,67 @@ button, .buttongroup > & min-width: 0 + + &.btn-danger + background-color: var(--color-error-dark) .buttongroup display: grid grid: min-content / auto-flow 1fr -input[type="submit"][disabled], -input[type="button"][disabled], -button[disabled], -.btn[disabled] +input[type="submit"][disabled]:not(.btn-link), +input[type="button"][disabled]:not(.btn-link), +button[disabled]:not(.btn-link), +.btn[disabled]:not(.btn-link) opacity: 0.3 background-color: var(--color-grey) cursor: default -input[type="submit"]:not([disabled]):hover, -input[type="button"]:not([disabled]):hover, -button:not([disabled]):hover, -.btn:not([disabled]):hover +input[type="submit"]:not([disabled]):not(.btn-link):hover, +input[type="button"]:not([disabled]):not(.btn-link):hover, +button:not([disabled]):not(.btn-link):hover, +.btn:not([disabled]):not(.btn-link):hover background-color: var(--color-light) color: white -.btn-primary + &.btn-danger + background-color: var(--color-error) + +.btn-primary:not(.btn-link) background-color: var(--color-primary) -.btn-info +.btn-info:not(.btn-link) background-color: var(--color-info) -.btn--small +.btn--small:not(.btn-link) padding: 4px 7px background-color: var(--color-darker) -input[type="submit"].btn-info:hover, -input[type="button"].btn-info:hover, -.btn-info:hover +input[type="submit"].btn-info:not(.btn-link):hover, +input[type="button"].btn-info:not(.btn-link):hover, +.btn-info:not(.btn-link):hover background-color: var(--color-grey) +.btn-link + font-family: var(--font-base) + outline: 0 + border: 0 + box-shadow: 0 + background: none + color: inherit + padding: 0 + min-width: unset + font-size: inherit + cursor: pointer + display: inline + text-decoration: underline + font-weight: 600 + font-style: inherit + transition: color .2s ease, background-color .2s ease + + &:not([disabled]):hover + color: var(--color-link-hover) + // GENERAL TABLE STYLES .table margin: 21px 0 @@ -312,11 +340,13 @@ input[type="button"].btn-info:hover, .table--striped .table__row:not(.no-stripe):not(.table__row--sum):nth-child(even) - background-color: rgba(0, 0, 0, 0.03) + .table__td + background-color: rgba(0, 0, 0, 0.03) .table--hover .table__row:not(.no-hover):not(.table__row--sum):not(.table__row--head):not(.table__row--foot):hover - background-color: rgba(0, 0, 0, 0.07) + .table__td + background-color: rgba(0, 0, 0, 0.07) .table__row--sum td.table__td::before content: 'Σ' @@ -345,6 +375,19 @@ input[type="button"].btn-info:hover, padding-right: 10px max-width: 300px +.table__td--number + width: min-content + padding-left: 0 + + .table--striped &, .table--hover & + vertical-align: middle + + .table__td-content + text-align: right + font-size: 0.9rem + font-weight: 600 + color: var(--color-fontsec) + .table__td font-size: 16px color: var(--color-font) @@ -390,6 +433,9 @@ input[type="button"].btn-info:hover, &.table__th-link::before display: none +.table__th--number + padding: 0 + @media (max-width: 1200px) .table th padding: 4px 6px @@ -634,11 +680,27 @@ section .heated --hotness: 0 - --red: calc(var(--hotness) * 200) - --green: calc(255 - calc(var(--hotness) * 255)) - --opacity: calc(calc(var(--red) / 600) + 0.1) - font-weight: var(--weight, 600) - background-color: rgba(var(--red), var(--green), 0, var(--opacity)) + + $hue: calc(120 - var(--hotness) * 120) + $opacity: calc(var(--hotness) * var(--hotness) / 3 + 0.1) + + background-color: hsla($hue, 75%, 50%, $opacity) !important + font-weight: calc(var(--hotness) * 200 + 400) + +.dual-heated + --hotness: 0 + + $hue: calc(240 - var(--hotness) * 120) + $opacity: calc(((var(--hotness) - 1) * (var(--hotness) - 1)) / 3 + 0.1) + + background-color: hsla($hue, 75%, 50%, $opacity) !important + font-weight: calc(((var(--hotness) - 1) * (var(--hotness) - 1)) * 200 + 400) + + &.degenerate + $hue: calc(240 + var(--hotness) * 60) + + background-color: hsla($hue, 75%, 50%, $opacity) !important + .uuid font-family: monospace @@ -791,6 +853,9 @@ th, td font-style: normal color: var(--color-font) +.course-event-note--hidden + display: none + .bound_explanation color: var(--color-fontsec) font-style: italic @@ -838,7 +903,7 @@ th, td right: 5px top: 5px -.occurrence--not-registered, .no-bonus +.occurrence--not-registered, .no-bonus, .allocation-course--excluded, .allocation-course--inactive text-decoration: line-through .result @@ -849,11 +914,11 @@ th, td dt, .dt font-weight: 600 - &.sec - font-style: italic - font-size: 0.9rem - font-weight: 600 - color: var(--color-fontsec) + &.sec + font-style: italic + font-size: 0.9rem + font-weight: 600 + color: var(--color-fontsec) dd, .dd margin-left: 12px @@ -861,6 +926,12 @@ th, td dd + dt, .dd + dt, dd + .dt, .dd + .dt margin-top: 17px +.deflist--no-grid + dt, .dt + font-weight: 600 + dd, .dd + margin-left: 12px + .explanation font-style: italic font-size: 0.9rem @@ -960,6 +1031,9 @@ th, td justify-content: space-between margin-bottom: 15px + &:empty + margin: 0 + // TABLE FOOTER .table-footer display: flex @@ -967,6 +1041,9 @@ th, td justify-content: space-between margin-top: 15px + &:empty + margin: 0 + // PAGINATION .pagination margin-top: 20px @@ -1136,7 +1213,7 @@ a.breadcrumbs__home font-size: 14px font-family: monospace -.func-field__wrapper +.func-field__wrapper, .allocation-missing-prios, .allocation-users__accept max-height: 75vh overflow: auto @@ -1193,3 +1270,91 @@ a.breadcrumbs__home text-align: right .text--center text-align: center + +.course__registration-status + margin-bottom: 12px + +.csv-parse-error + white-space: pre-wrap + font-family: monospace + overflow: auto + max-height: 75vh + +.labeled-checkbox + display: grid + grid-gap: 0 7px + grid-template-columns: 20px 1fr + grid-template-areas: "checkbox label" + + &__checkbox + grid-area: checkbox + place-self: start center + line-height: 0 + + &__label + grid-area: label + +.news__system-messages + overflow-y: auto + max-height: 75vh + +.news__system-message-detail + font-style: italic + font-size: 0.9rem + font-weight: 600 + color: var(--color-fontsec) + + .news__system-message-content + & + margin-top: 10px + +.news__system-message + border-left: 3px solid var(--color-info) + padding-left: 17px + background-color: rgba(0,0,0,0.015) + + & + .news__system-message + margin-top: 17px + + &--info + border-left-color: var(--color-info) + + &--error + border-left-color: var(--color-error) + + &--warning + border-left-color: var(--color-warning) + + &--success + border-left-color: var(--color-success) + +.faq__question + font-size: 18px + font-weight: 400 + + margin: 0 + +.faq__answer + margin-left: 17px + +:not(.show-hide--collapsed) > .faq__answer + margin-top: 7px + +.faq__section + padding-bottom: 10px + + &:last-child, &.show-hide--collapsed + border-bottom: none + padding-bottom: 0 + + & + section:not(.faq__section) + border-top: 1px solid #d3d3d3 + padding-top: 30px + +.faq__section + .faq__section + margin-top: 10px + +.faq__question-link + opacity: 0.2 + + &:hover + opacity: 1 diff --git a/frontend/src/lib/storage-manager/storage-manager.js b/frontend/src/lib/storage-manager/storage-manager.js index d2461518c..418d92224 100644 --- a/frontend/src/lib/storage-manager/storage-manager.js +++ b/frontend/src/lib/storage-manager/storage-manager.js @@ -9,9 +9,10 @@ export const LOCATION = { LOCAL: 'local', SESSION: 'session', WINDOW: 'window', + HISTORY: 'history', }; -const LOCATION_SHADOWING = [ LOCATION.WINDOW, LOCATION.SESSION, LOCATION.LOCAL ]; +const LOCATION_SHADOWING = [ LOCATION.HISTORY, LOCATION.WINDOW, LOCATION.SESSION, LOCATION.LOCAL ]; export class StorageManager { @@ -26,7 +27,16 @@ export class StorageManager { constructor(namespace, version, options) { this._debugLog('constructor', namespace, version, options); - this.namespace = namespace; + if (typeof namespace === 'object') { + let sep = '_'; + const namespace_arr = Array.from(namespace); + while (namespace_arr.some(str => str.includes(sep))) + sep = sep + '_'; + + this.namespace = Array.from(namespace).join(sep); + } else { + this.namespace = namespace; + } this.version = semver.valid(version); if (!namespace) { @@ -48,7 +58,7 @@ export class StorageManager { throw new Error('Cannot setup StorageManager without window or global'); if (this._options.encryption) { - [LOCATION.LOCAL, LOCATION.SESSION].forEach((location) => { + [LOCATION.LOCAL, LOCATION.SESSION, LOCATION.HISTORY].forEach((location) => { const encryption = this._options.encryption.all || this._options.encryption[location]; if (encryption) this._requestStorageKey({ location: location, encryption: encryption }); }); @@ -70,17 +80,21 @@ export class StorageManager { switch (location) { case LOCATION.LOCAL: { - this._saveToLocalStorage(this._updateStorage(this._getFromLocalStorage(options), { [key]: value }, LOCATION.LOCAL, options)); + this._saveToLocalStorage({ ...this._getFromLocalStorage(options), [key]: value}, options); break; } case LOCATION.SESSION: { - this._saveToSessionStorage(this._updateStorage(this._getFromSessionStorage(options), { [key]: value }, LOCATION.SESSION, options)); + this._saveToSessionStorage({ ...this._getFromSessionStorage(options), [key]: value}, options); break; } case LOCATION.WINDOW: { this._saveToWindow({ ...this._getFromWindow(), [key]: value }); break; } + case LOCATION.HISTORY: { + this._saveToHistory({ ...this._getFromHistory(), [key]: value }, options); + break; + } default: console.error('StorageManager.save cannot save item with unsupported location'); } @@ -112,6 +126,10 @@ export class StorageManager { val = this._getFromWindow()[key]; break; } + case LOCATION.HISTORY: { + val = this._getFromHistory(options)[key]; + break; + } default: console.error('StorageManager.load cannot load item with unsupported location'); } @@ -138,14 +156,14 @@ export class StorageManager { delete val[key]; - return this._saveToLocalStorage(val); + return this._saveToLocalStorage(val, options); } case LOCATION.SESSION: { let val = this._getFromSessionStorage(options); delete val[key]; - return this._saveToSessionStorage(val); + return this._saveToSessionStorage(val, options); } case LOCATION.WINDOW: { let val = this._getFromWindow(); @@ -154,6 +172,14 @@ export class StorageManager { return this._saveToWindow(val); } + case LOCATION.HISTORY: { + let val = this._getFromHistory(options); + + delete val[key]; + + return this._saveToHistory(val, options); + } + default: console.error('StorageManager.load cannot load item with unsupported location'); } @@ -177,6 +203,8 @@ export class StorageManager { return this._clearSessionStorage(); case LOCATION.WINDOW: return this._clearWindow(); + case LOCATION.HISTORY: + return this._clearHistory(options && options.history); default: console.error('StorageManager.clear cannot clear with unsupported location'); } @@ -185,7 +213,7 @@ export class StorageManager { } - _getFromLocalStorage(options=this._options) { + _getFromLocalStorage(options) { this._debugLog('_getFromLocalStorage', options); let state; @@ -210,7 +238,7 @@ export class StorageManager { } } - _saveToLocalStorage(state) { + _saveToLocalStorage(state, options) { this._debugLog('_saveToLocalStorage', state); if (!state) @@ -223,8 +251,8 @@ export class StorageManager { } else { versionedState = { version: this.version, ...state }; } - - window.localStorage.setItem(this.namespace, JSON.stringify(versionedState)); + + window.localStorage.setItem(this.namespace, JSON.stringify(this._updateStorage({}, versionedState, LOCATION.LOCAL, options))); } _clearLocalStorage() { @@ -240,10 +268,10 @@ export class StorageManager { if (!this._global || !this._global.App) return {}; - if (!this._global.App.Storage) - this._global.App.Storage = {}; + if (!this._global.App.Storage || !this._global.App.Storage[this.namespace]) + return {}; - return this._global.App.Storage; + return this._global.App.Storage[this.namespace]; } _saveToWindow(value) { @@ -274,8 +302,71 @@ export class StorageManager { } } + _getFromHistory(options) { + this._debugLog('_getFromHistory'); - _getFromSessionStorage(options=this._options) { + if (!this._global || !this._global.history) + return {}; + + if (!this._global.history.state || !this._global.history.state[this.namespace]) + return {}; + + return this._getFromStorage(this._global.history.state[this.namespace], LOCATION.HISTORY, options); + } + + _saveToHistory(value, options) { + this._debugLog('_saveToHistory', options); + + + if (!this._global || !this._global.history) { + throw new Error('StorageManager._saveToHistory called when window.history is not available'); + } + + const push = (options.history && typeof options.history.push !== 'undefined') ? !!options.history.push : true; + const title = (options.history && options.history.title) || (this._global.document && this._global.document.title) || ''; + const url = (options.history && options.history.url) || (this._global.document && this._global.document.location); + + const state = this._global.history.state || {}; + state[this.namespace] = this._updateStorage({}, value, LOCATION.HISTORY, options); + + this._debugLog('_saveToHistory', { state: state, push: push, title: title, url: url}); + + if (push) + this._global.history.pushState(state, title, url); + else + this._global.history.replaceState(state, title, url); + } + + _clearHistory(options) { + this._debugLog('_clearHistory', options); + + if (!this._global || !this._global.history) { + throw new Error('StorageManager._clearHistory called when window.history is not available'); + } + + const push = (options.history && typeof options.history.push !== 'undefined' ? !!options.history.push : true) || true; + const title = (options.history && options.history.title) || (this._global.document && this._global.document.title) || ''; + const url = (options.history && options.history.url) || (this._global.document && this._global.document.location); + + const state = this._global.history.state || {}; + delete state[this.namespace]; + + if (push) + this._global.history.pushState(state, title, url); + else + this._global.history.replaceState(state, title, url); + } + + addHistoryListener(listener, options=this._options, ...args) { + const modified_listener = (function(event, ...listener_args) { // eslint-disable-line no-unused-vars + this._global.setTimeout(() => listener(this._getFromHistory(options), ...listener_args)); + }).bind(this); + + this._global.addEventListener('popstate', modified_listener, args); + } + + + _getFromSessionStorage(options) { this._debugLog('_getFromSessionStorage', options); let state; @@ -300,7 +391,7 @@ export class StorageManager { } } - _saveToSessionStorage(state) { + _saveToSessionStorage(state, options) { this._debugLog('_saveToSessionStorage', state); if (!state) @@ -314,7 +405,7 @@ export class StorageManager { versionedState = { version: this.version, ...state }; } - window.sessionStorage.setItem(this.namespace, JSON.stringify(versionedState)); + window.sessionStorage.setItem(this.namespace, JSON.stringify(this._updateStorage({}, versionedState, LOCATION.SESSION, options))); } _clearSessionStorage() { @@ -324,10 +415,10 @@ export class StorageManager { } - _getFromStorage(storage, location, options=this._options) { + _getFromStorage(storage, location, options) { this._debugLog('_getFromStorage', storage, location, options); - const encryption = options.encryption && (options.encryption.all || options.encryption[location]); + const encryption = options && options.encryption && (options.encryption.all || options.encryption[location]); if (encryption && storage.encryption) { return { ...storage, ...JSON.parse(decrypt(storage.encryption.ciphertext, this._encryptionKey[location]) || null) }; } else { @@ -335,10 +426,10 @@ export class StorageManager { } } - _updateStorage(storage, update, location, options=this._options) { + _updateStorage(storage, update, location, options) { this._debugLog('_updateStorage', storage, update, location, options); - const encryption = options.encryption && (options.encryption.all || options.encryption[location]); + const encryption = options && options.encryption && (options.encryption.all || options.encryption[location]); if (encryption && storage.encryption) { const updatedDecryptedStorage = { ...JSON.parse(decrypt(storage.encryption.ciphertext, this._encryptionKey[location]) || null), ...update }; console.log('updatedDecryptedStorage', updatedDecryptedStorage); @@ -357,13 +448,13 @@ export class StorageManager { const enc = this.load('encryption', { ...options, encryption: false }); const requestBody = { type : options.encryption, - length : 42, + length : sodium.crypto_secretbox_KEYBYTES, salt : enc.salt, timestamp : enc.timestamp, }; this._global.App.httpClient.post({ - url: '../../../../../../user/storage-key', // TODO use APPROOT instead + url: '/user/storage-key', headers: { 'Content-Type' : HttpClient.ACCEPT.JSON, 'Accept' : HttpClient.ACCEPT.JSON, @@ -381,11 +472,10 @@ export class StorageManager { }).catch(console.error); } - _debugLog() { + _debugLog() {} // _debugLog(fName, ...args) { - // console.log(`[DEBUGLOG] StorageManager.${fName}`, { args: args, instance: this }); - } - + // console.log(`[DEBUGLOG] StorageManager.${fName}`, { args: args, instance: this }); + // } } diff --git a/frontend/src/services/html-helpers/html-helpers.js b/frontend/src/services/html-helpers/html-helpers.js index 5799fee24..48f5cc082 100644 --- a/frontend/src/services/html-helpers/html-helpers.js +++ b/frontend/src/services/html-helpers/html-helpers.js @@ -17,7 +17,7 @@ export class HtmlHelpers { idPrefix = this._getIdPrefix(); this._prefixIds(element, idPrefix); } - return Promise.resolve({ idPrefix, element }); + return Promise.resolve({ idPrefix, element, headers: response.headers }); }, Promise.reject, ).catch(console.error); diff --git a/frontend/src/services/http-client/http-client.js b/frontend/src/services/http-client/http-client.js index 4ea725d2f..274f86cdb 100644 --- a/frontend/src/services/http-client/http-client.js +++ b/frontend/src/services/http-client/http-client.js @@ -15,6 +15,27 @@ export class HttpClient { } } + _baseUrl; + + setBaseUrl(baseUrl) { + if (typeof this._baseUrl !== 'undefined') { + throw new Error('HttpClient baseUrl is already set'); + } + + this._baseUrl = baseUrl; + } + + _defaultUrl; + + setDefaultUrl(defaultUrl) { + if (typeof this._defaultUrl !== 'undefined') { + throw new Error('HttpClient defaultUrl is already set'); + } + + this._defaultUrl = defaultUrl; + } + + get(args) { args.method = 'GET'; return this._fetch(args); @@ -28,12 +49,17 @@ export class HttpClient { } _fetch(options) { + options.url = (options.url && options.url.href) || options.url || this._defaultUrl; + + if (this._baseUrl && options.url && options.url.substring(0,1) === '/' && options.url.substring(0,2) !== '//') + options.url = this._baseUrl + (this._baseUrl.substring(this._baseUrl.substring.length - 1) === '/' ? '' : '/') + options.url.substring(1,0); + const requestOptions = { credentials: 'same-origin', ...options, }; - return fetch(options.url, requestOptions) + return fetch(options.url || window.location.href, requestOptions) .then( (response) => { this._responseInterceptors.forEach((interceptor) => interceptor(response, options)); diff --git a/frontend/src/utils/alerts/alerts.js b/frontend/src/utils/alerts/alerts.js index 6c4a41a40..dcecc915b 100644 --- a/frontend/src/utils/alerts/alerts.js +++ b/frontend/src/utils/alerts/alerts.js @@ -155,7 +155,7 @@ export class Alerts { alertCloser.classList.add(ALERT_CLOSER_CLASS); const alertIcon = document.createElement('div'); - alertIcon.classList.add(ALERT_ICON_CLASS, 'fas', 'fa-fw', 'fa-' + icon); + alertIcon.classList.add(ALERT_ICON_CLASS, 'fas', 'fa-' + icon); const alertContent = document.createElement('div'); alertContent.classList.add(ALERT_CONTENT_CLASS); diff --git a/frontend/src/utils/asidenav/asidenav.sass b/frontend/src/utils/asidenav/asidenav.sass index 4ed526a21..c6d6b070e 100644 --- a/frontend/src/utils/asidenav/asidenav.sass +++ b/frontend/src/utils/asidenav/asidenav.sass @@ -232,12 +232,22 @@ .asidenav__nested-list min-width: 200px +.asidenav__nested-list--unavailable + font-size: 0.9rem + color: var(--color-fontsec) + font-weight: 600 + padding: 7px + min-width: 200px + @media (max-width: 425px) .asidenav__list-item padding-left: 10px .asidenav__nested-list display: none + + .asidenav__nested-list--unavailable + display: none .asidenav__nested-list-item position: relative @@ -317,10 +327,9 @@ color: var(--color-font) padding: 0 - .asidenav__nested-list, - .asidenav__link-label + .asidenav__nested-list, .asidenav__link-label, .asidenav__nested-list--unavailable display: none - + .asidenav__list-item--active .asidenav__link-wrapper background-color: var(--color-lightwhite) diff --git a/frontend/src/utils/async-table/async-table.js b/frontend/src/utils/async-table/async-table.js index e40ecc86d..1503c6691 100644 --- a/frontend/src/utils/async-table/async-table.js +++ b/frontend/src/utils/async-table/async-table.js @@ -13,7 +13,9 @@ const INPUT_DEBOUNCE = 600; const FILTER_DEBOUNCE = 100; const HEADER_HEIGHT = 80; -const ASYNC_TABLE_LOCAL_STORAGE_KEY = 'ASYNC_TABLE'; +const ASYNC_TABLE_STORAGE_KEY = 'ASYNC_TABLE'; +const ASYNC_TABLE_STORAGE_VERSION = '2.0.0'; + const ASYNC_TABLE_SCROLLTABLE_SELECTOR = '.scrolltable'; const ASYNC_TABLE_INITIALIZED_CLASS = 'async-table--initialized'; const ASYNC_TABLE_LOADING_CLASS = 'async-table--loading'; @@ -47,7 +49,10 @@ export class AsyncTable { }; _ignoreRequest = false; - _storageManager = new StorageManager(ASYNC_TABLE_LOCAL_STORAGE_KEY, '1.0.0', { location: LOCATION.WINDOW }); + _windowStorage; + _historyStorage; + + _active = true; constructor(element, app) { if (!element) { @@ -79,21 +84,56 @@ export class AsyncTable { this._cssIdPrefix = findCssIdPrefix(rawTableId); this._asyncTableId = rawTableId.replace(this._cssIdPrefix, ''); + if (!this._asyncTableId) { + throw new Error('Async Table cannot be set up without an ident!'); + } + + this._windowStorage = new StorageManager([ASYNC_TABLE_STORAGE_KEY, this._asyncTableId], ASYNC_TABLE_STORAGE_VERSION, { location: LOCATION.WINDOW }); + this._historyStorage = new StorageManager([ASYNC_TABLE_STORAGE_KEY, this._asyncTableId], ASYNC_TABLE_STORAGE_VERSION, { location: LOCATION.HISTORY }); + // find scrolltable wrapper this._scrollTable = this._element.querySelector(ASYNC_TABLE_SCROLLTABLE_SELECTOR); if (!this._scrollTable) { throw new Error('Async Table cannot be set up without a scrolltable element!'); } - this._setupTableFilter(); - this._processStorage(); - // clear currentTableUrl from previous requests - this._storageManager.remove('currentTableUrl'); + this._setupTableFilter(); + + this._windowStorage.remove('currentTableUrl'); + + if (!('currentTableUrl' in this._element.dataset)) { + this._element.dataset['currentTableUrl'] = document.location.href; + this._historyStorage.save('currentTableUrl', document.location.href, { location: LOCATION.HISTORY, history: { push: false } }); + } + + this._historyListener(); + + this._historyStorage.addHistoryListener(this._historyListener.bind(this)); // mark initialized - this._element.classList.add(ASYNC_TABLE_INITIALIZED_CLASS); + if (this._active) + this._element.classList.add(ASYNC_TABLE_INITIALIZED_CLASS); + } + + _historyListener(historyState) { + if (!this._active) + return; + + const windowUrl = this._element.dataset['currentTableUrl']; + const historyUrl = historyState ? historyState['currentTableUrl'] : this._historyStorage.load('currentTableUrl'); + this._debugLog('_historyListener', historyState, windowUrl, historyUrl); + + if (this._isEquivalentUrl(windowUrl, historyUrl)) + return; + + this._debugLog('_historyListener', historyUrl); + this._updateTableFrom(historyUrl || document.location.href, undefined, true); + } + + _isEquivalentUrl(a, b) { + return a === b; } start() { @@ -113,7 +153,7 @@ export class AsyncTable { this._ths.forEach((th) => { th.clickHandler = (event) => { - this._storageManager.save('horizPos', (this._scrollTable || {}).scrollLeft); + this._windowStorage.save('horizPos', (this._scrollTable || {}).scrollLeft); this._linkClickHandler(event); }; th.element.addEventListener('click', th.clickHandler); @@ -135,7 +175,7 @@ export class AsyncTable { left: this._scrollTable.offsetLeft || 0, behavior: 'smooth', }; - this._storageManager.save('scrollTo', scrollTo); + this._windowStorage.save('scrollTo', scrollTo); } this._linkClickHandler(event); }; @@ -256,7 +296,7 @@ export class AsyncTable { const prefix = findCssIdPrefix(focusedInput.id); const focusId = focusedInput.id.replace(prefix, ''); callback = function(wrapper) { - const idPrefix = this._storageManager.load('cssIdPrefix'); + const idPrefix = this._windowStorage.load('cssIdPrefix'); const toBeFocused = wrapper.querySelector('#' + idPrefix + focusId); if (toBeFocused) { toBeFocused.focus(); @@ -268,34 +308,33 @@ export class AsyncTable { } _serializeTableFilterToURL(tableFilterForm) { - const url = new URL(this._storageManager.load('currentTableUrl') || window.location.href); + const url = new URL(this._windowStorage.load('currentTableUrl') || window.location.href); // create new FormData and format any date values const formData = Datepicker.unformatAll(tableFilterForm, new FormData(tableFilterForm)); - for (var k of url.searchParams.keys()) { - url.searchParams.delete(k); - } + this._debugLog('_serializeTableFilterToURL', Array.from(formData.entries()), url.href); - for (var kv of formData.entries()) { - url.searchParams.append(kv[0], kv[1]); - } + const searchParams = new URLSearchParams(Array.from(formData.entries())); + url.search = searchParams.toString(); + + this._debugLog('_serializeTableFilterToURL', url.href); return url; } _processStorage() { - const scrollTo = this._storageManager.load('scrollTo'); + const scrollTo = this._windowStorage.load('scrollTo'); if (scrollTo && this._scrollTable) { window.scrollTo(scrollTo); } - this._storageManager.remove('scrollTo'); + this._windowStorage.remove('scrollTo'); - const horizPos = this._storageManager.load('horizPos'); + const horizPos = this._windowStorage.load('horizPos'); if (horizPos && this._scrollTable) { this._scrollTable.scrollLeft = horizPos; } - this._storageManager.remove('horizPos'); + this._windowStorage.remove('horizPos'); } _removeListeners() { @@ -330,7 +369,7 @@ export class AsyncTable { } _changePagesizeHandler = () => { - const url = new URL(this._storageManager.load('currentTableUrl') || window.location.href); + const url = new URL(this._windowStorage.load('currentTableUrl') || window.location.href); // create new FormData and format any date values const formData = Datepicker.unformatAll(this._pagesizeForm, new FormData(this._pagesizeForm)); @@ -347,7 +386,9 @@ export class AsyncTable { } // fetches new sorted element from url with params and replaces contents of current element - _updateTableFrom(url, callback) { + _updateTableFrom(url, callback, isPopState) { + url = new URL(url); + const cancelPendingUpdates = (() => { this._cancelPendingUpdates.forEach(f => f()); }).bind(this); @@ -372,23 +413,33 @@ export class AsyncTable { return false; } - this._storageManager.save('currentTableUrl', url.href); + if (!isPopState) + this._historyStorage.save('currentTableUrl', url.href, { location: LOCATION.HISTORY, history: { push: true, url: response.headers.get('DB-Table-Canonical-URL') || url.href } }); + + this._windowStorage.save('currentTableUrl', url.href); // reset table this._removeListeners(); + this._active = false; this._element.classList.remove(ASYNC_TABLE_INITIALIZED_CLASS); + this._element.dataset['currentTableUrl'] = url.href; // update table with new this._element.innerHTML = response.element.innerHTML; this._app.utilRegistry.initAll(this._element); if (callback && typeof callback === 'function') { - this._storageManager.save('cssIdPrefix', response.idPrefix); + this._windowStorage.save('cssIdPrefix', response.idPrefix); callback(this._element); - this._storageManager.remove('cssIdPrefix'); + this._windowStorage.remove('cssIdPrefix'); } }).catch((err) => console.error(err) ).finally(() => this._element.classList.remove(ASYNC_TABLE_LOADING_CLASS)); } + + _debugLog() {} + // _debugLog(fName, ...args) { + // console.log(`[DEBUGLOG] AsyncTable.${fName}`, { args: args, instance: this }); + // } } diff --git a/frontend/src/utils/async-table/async-table.spec.js b/frontend/src/utils/async-table/async-table.spec.js index d72b4c1ec..7f008ac49 100644 --- a/frontend/src/utils/async-table/async-table.spec.js +++ b/frontend/src/utils/async-table/async-table.spec.js @@ -20,6 +20,7 @@ describe('AsyncTable', () => { const element = document.createElement('div'); const scrollTable = document.createElement('div'); const table = document.createElement('table'); + table.id = 'ident'; scrollTable.classList.add('scrolltable'); scrollTable.appendChild(table); element.appendChild(scrollTable); diff --git a/frontend/src/utils/exam-correct/exam-correct.js b/frontend/src/utils/exam-correct/exam-correct.js index 80b0592d7..edee3bc47 100644 --- a/frontend/src/utils/exam-correct/exam-correct.js +++ b/frontend/src/utils/exam-correct/exam-correct.js @@ -7,7 +7,6 @@ import moment from 'moment'; import './exam-correct.sass'; -const EXAM_CORRECT_URL_POST = 'correct'; const EXAM_CORRECT_HEADERS = { 'Content-Type': HttpClient.ACCEPT.JSON, 'Accept': HttpClient.ACCEPT.JSON, @@ -198,7 +197,6 @@ export class ExamCorrect { const body = this._toRequestBody(this._userInput.value); this._app.httpClient.post({ - url: EXAM_CORRECT_URL_POST, headers: EXAM_CORRECT_HEADERS, body: JSON.stringify(body), }).then( @@ -290,7 +288,6 @@ export class ExamCorrect { const body = this._toRequestBody(userId || user, results, result); this._app.httpClient.post({ - url: EXAM_CORRECT_URL_POST, headers: EXAM_CORRECT_HEADERS, body: JSON.stringify(body), }).then( @@ -522,7 +519,6 @@ export class ExamCorrect { const body = this._toRequestBody(listItem.getAttribute(EXAM_CORRECT_USER_ATTR), results.partResults, results.result); this._app.httpClient.post({ - url: EXAM_CORRECT_URL_POST, headers: EXAM_CORRECT_HEADERS, body: JSON.stringify(body), }).then( diff --git a/frontend/src/utils/form/interactive-fieldset.js b/frontend/src/utils/form/interactive-fieldset.js index 30e2bead9..438e41012 100644 --- a/frontend/src/utils/form/interactive-fieldset.js +++ b/frontend/src/utils/form/interactive-fieldset.js @@ -5,10 +5,13 @@ const INTERACTIVE_FIELDSET_UTIL_TARGET_SELECTOR = '.interactive-fieldset__target const INTERACTIVE_FIELDSET_INITIALIZED_CLASS = 'interactive-fieldset--initialized'; const INTERACTIVE_FIELDSET_CHILD_SELECTOR = 'input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled])'; +let fieldsetCounter = 0; + @Utility({ selector: '[uw-interactive-fieldset]', }) export class InteractiveFieldset { + fieldsetIdent = (fieldsetCounter++).toString(); _element; @@ -56,21 +59,23 @@ export class InteractiveFieldset { this.target = this._element; } - this.childInputs = Array.from(this._element.querySelectorAll(INTERACTIVE_FIELDSET_CHILD_SELECTOR)); + this.childInputs = Array.from(this._element.querySelectorAll(INTERACTIVE_FIELDSET_CHILD_SELECTOR)).filter(child => child.closest('[uw-interactive-fieldset]') === this._element); // add event listener - const observer = new MutationObserver(() => this._updateVisibility()); + const observer = new MutationObserver(this._updateVisibility.bind(this)); observer.observe(this.conditionalInput, { attributes: true, attributeFilter: ['data-interactive-fieldset-hidden'] }); - this.conditionalInput.addEventListener('input', () => this._updateVisibility()); - - // initial visibility update - this._updateVisibility(); + this.conditionalInput.addEventListener('input', this._updateVisibility.bind(this)); // mark as initialized this._element.classList.add(INTERACTIVE_FIELDSET_INITIALIZED_CLASS); } + start() { + // initial visibility update + this._updateVisibility(); + } + destroy() { // TODO } @@ -78,7 +83,20 @@ export class InteractiveFieldset { _updateVisibility() { const active = this._matchesConditionalValue() && !this.conditionalInput.dataset.interactiveFieldsetHidden; - this.target.classList.toggle('hidden', !active); + let hiddenBy = (this.target.dataset.interactiveFieldsetHiddenBy || '').split(',').filter(str => str.length !== 0); + + if (active) + hiddenBy = hiddenBy.filter(ident => ident !== this.fieldsetIdent); + else if (hiddenBy.every(ident => ident !== this.fieldsetIdent)) + hiddenBy = [ ...hiddenBy, this.fieldsetIdent ]; + + if (hiddenBy.length !== 0) { + this.target.dataset.interactiveFieldsetHiddenBy = hiddenBy.join(','); + this.target.classList.add('hidden'); + } else { + delete this.target.dataset['interactiveFieldsetHiddenBy']; + this.target.classList.remove('hidden'); + } this.childInputs.forEach((el) => this._updateChildVisibility(el, active)); } @@ -98,6 +116,9 @@ export class InteractiveFieldset { if (this._isCheckbox()) { matches = this.conditionalInput.checked === true; + } else if (this._isRadio()) { + const radios = Array.from(this.conditionalInput.querySelectorAll('input[type=radio]')); + matches = radios.some(radio => radio.checked && radio.value === this.conditionalValue); } else { matches = this.conditionalInput.value === this.conditionalValue; } @@ -112,4 +133,8 @@ export class InteractiveFieldset { _isCheckbox() { return this.conditionalInput.getAttribute('type') === 'checkbox'; } + + _isRadio() { + return !!this.conditionalInput.querySelector('input[type=radio]'); + } } diff --git a/frontend/src/utils/hide-columns/hide-columns.sass b/frontend/src/utils/hide-columns/hide-columns.sass index dd4658954..47ff2dd04 100644 --- a/frontend/src/utils/hide-columns/hide-columns.sass +++ b/frontend/src/utils/hide-columns/hide-columns.sass @@ -58,5 +58,8 @@ display: block clear: both + &:empty + margin: 0 + .hide-columns--hidden-cell display: none diff --git a/frontend/src/utils/inputs/checkbox.sass b/frontend/src/utils/inputs/checkbox.sass index ab7f0cd11..3842e1bb9 100644 --- a/frontend/src/utils/inputs/checkbox.sass +++ b/frontend/src/utils/inputs/checkbox.sass @@ -89,7 +89,7 @@ \:checked + label::before background-color: white - [disabled] + label + [disabled] + label, [readonly] + label pointer-events: none border: none opacity: 0.6 diff --git a/frontend/src/utils/inputs/inputs.sass b/frontend/src/utils/inputs/inputs.sass index ec9581ff7..7288f47e7 100644 --- a/frontend/src/utils/inputs/inputs.sass +++ b/frontend/src/utils/inputs/inputs.sass @@ -60,13 +60,21 @@ grid-column: 1 .form-group--has-error - background-color: rgba(255, 0, 0, 0.1) + background-color: rgba(140, 7, 7, 0.05) + + .form-group-label + border-left: 2px solid var(--color-error) + align-self: stretch + padding-left: 7px input, textarea border-color: var(--color-error) !important .form-error display: block + font-weight: 600 + color: var(--color-error) + margin: 7px 0 .form-error display: none diff --git a/frontend/src/utils/inputs/radio-group.sass b/frontend/src/utils/inputs/radio-group.sass index 367a05702..32313b488 100644 --- a/frontend/src/utils/inputs/radio-group.sass +++ b/frontend/src/utils/inputs/radio-group.sass @@ -4,52 +4,52 @@ .radio-group display: flex -.radio - position: relative - display: inline-block + .radio + position: relative + display: inline-block - [type='radio'] - position: fixed - top: -1px - left: -1px - width: 1px - height: 1px - overflow: hidden + [type='radio'] + position: fixed + top: -1px + left: -1px + width: 1px + height: 1px + overflow: hidden - label - display: block - height: 34px - min-width: 42px - line-height: 34px - text-align: center - padding: 0 13px - background-color: #f3f3f3 - box-shadow: inset 2px 1px 2px 1px rgba(50, 50, 50, 0.05) - color: var(--color-font) - cursor: pointer + label + display: block + height: 34px + min-width: 42px + line-height: 34px + text-align: center + padding: 0 13px + background-color: #f3f3f3 + box-shadow: inset 2px 1px 2px 1px rgba(50, 50, 50, 0.05) + color: var(--color-font) + cursor: pointer - \:checked + label - background-color: var(--color-primary) - color: var(--color-lightwhite) - box-shadow: inset -2px -1px 2px 1px rgba(255, 255, 255, 0.15) + \:checked + label + background-color: var(--color-primary) + color: var(--color-lightwhite) + box-shadow: inset -2px -1px 2px 1px rgba(255, 255, 255, 0.15) - \:focus + label - border-color: #3273dc - box-shadow: 0 0 0.125em 0 rgba(50, 115, 220, 0.8) - outline: 0 + \:focus + label + border-color: #3273dc + box-shadow: 0 0 0.125em 0 rgba(50, 115, 220, 0.8) + outline: 0 - [disabled] + label - pointer-events: none - border: none - opacity: 0.6 - filter: grayscale(1) + [disabled] + label + pointer-events: none + border: none + opacity: 0.6 + filter: grayscale(1) -.radio:first-child - label - border-top-left-radius: 4px - border-bottom-left-radius: 4px + .radio:first-child + label + border-top-left-radius: 4px + border-bottom-left-radius: 4px -.radio:last-child - label - border-top-right-radius: 4px - border-bottom-right-radius: 4px + .radio:last-child + label + border-top-right-radius: 4px + border-bottom-right-radius: 4px diff --git a/frontend/src/utils/inputs/radio.sass b/frontend/src/utils/inputs/radio.sass index 0603875aa..f68943142 100644 --- a/frontend/src/utils/inputs/radio.sass +++ b/frontend/src/utils/inputs/radio.sass @@ -38,7 +38,7 @@ box-shadow: 0 0 0.125em 0 rgba(50, 115, 220, 0.8) outline: 0 - [disabled] + label + [disabled] + label, [readonly] + label pointer-events: none border: none opacity: 0.6 diff --git a/frontend/src/utils/mass-input/mass-input.js b/frontend/src/utils/mass-input/mass-input.js index dee124a1a..9e6a19d01 100644 --- a/frontend/src/utils/mass-input/mass-input.js +++ b/frontend/src/utils/mass-input/mass-input.js @@ -1,3 +1,5 @@ +/* global global:writable */ + import { Utility } from '../../core/utility'; import { Datepicker } from '../form/datepicker'; import './mass-input.sass'; @@ -7,6 +9,11 @@ const MASS_INPUT_ADD_CELL_SELECTOR = '.massinput__cell--add'; const MASS_INPUT_SUBMIT_BUTTON_CLASS = 'massinput__submit-button'; const MASS_INPUT_INITIALIZED_CLASS = 'mass-input--initialized'; +const MASS_INPUT_ADD_CHANGE_FIELD_SELECTOR = 'select, input[type=radio]'; + +// const MASS_INPUT_SAFETY_SUBMITTED_CLASS = 'massinput--safety-submitted'; +// const MASS_INPUT_SAFETY_SUBMITTED_TIMEOUT = 1000; + @Utility({ selector: '[uw-mass-input]', }) @@ -14,11 +21,14 @@ export class MassInput { _element; _app; + _global; _massInputId; _massInputFormSubmitHandler; _massInputForm; + _changedAdd = new Array(); + constructor(element, app) { if (!element) { throw new Error('Mass Input utility cannot be setup without an element!'); @@ -27,6 +37,14 @@ export class MassInput { this._element = element; this._app = app; + if (global !== undefined) + this._global = global; + else if (window !== undefined) + this._global = window; + else + throw new Error('Cannot setup Mass Input utility without window or global'); + + if (this._element.classList.contains(MASS_INPUT_INITIALIZED_CLASS)) { return false; } @@ -47,8 +65,10 @@ export class MassInput { this._setupSubmitButton(button); }); - this._massInputForm.addEventListener('submit', this._massInputFormSubmitHandler); - this._massInputForm.addEventListener('keypress', this._keypressHandler); + this._massInputForm.addEventListener('submit', this._massInputFormSubmitHandler.bind(this)); + this._massInputForm.addEventListener('keypress', this._keypressHandler.bind(this)); + + Array.from(this._element.querySelectorAll(MASS_INPUT_ADD_CELL_SELECTOR)).forEach(this._setupChangedHandlers.bind(this)); // mark initialized this._element.classList.add(MASS_INPUT_INITIALIZED_CLASS); @@ -58,6 +78,26 @@ export class MassInput { this._reset(); } + _setupChangedHandlers(addCell) { + Array.from(addCell.querySelectorAll(MASS_INPUT_ADD_CHANGE_FIELD_SELECTOR)).forEach(inputElem => { + if (inputElem.closest('[uw-mass-input]') !== this._element) + return; + + inputElem.addEventListener('change', () => { this._changedAdd.push(addCell); }); + }); + } + + _unsafeAddCells() { + let changedAdd = this._changedAdd; + + Array.from(this._element.querySelectorAll(MASS_INPUT_ADD_CELL_SELECTOR)).forEach(addCell => addCell.querySelectorAll('input:not([type=checkbox]):not([type=radio])').forEach(inputElem => { + if (inputElem.closest('[uw-mass-input]') === this._element && inputElem.value !== '' && (inputElem.defaultValue || inputElem.getAttribute('value')) !== inputElem.value) + changedAdd.push(addCell); + })); + + return changedAdd; + } + _makeSubmitHandler() { const method = this._massInputForm.getAttribute('method') || 'POST'; const url = this._massInputForm.getAttribute('action') || window.location.href; @@ -69,31 +109,58 @@ export class MassInput { } return (event) => { - let activeElement; + let submitButton; + let isAddCell; + + let isMassInputSubmit = (() => { + let activeElement; - // check if event occured from either a mass input add/delete button or - // from inside one of massinput's inputs (i.e. a child is focused/active) - activeElement = this._element.querySelector(':focus, :active'); + // check if event occured from either a mass input add/delete button or + // from inside one of massinput's inputs (i.e. a child is focused/active) + activeElement = this._element.querySelector(':focus, :active'); - if (!activeElement) { - return false; + if (!activeElement) { + return false; + } + + // find the according massinput cell thats hosts the element that triggered the submit + const massInputCell = activeElement.closest(MASS_INPUT_CELL_SELECTOR); + if (!massInputCell) { + return false; + } + + submitButton = massInputCell.querySelector('.' + MASS_INPUT_SUBMIT_BUTTON_CLASS); + if (!submitButton) { + return false; + } + + isAddCell = massInputCell.matches(MASS_INPUT_ADD_CELL_SELECTOR); + const submitButtonIsActive = submitButton.matches(':focus, :active'); + // if the cell is not an add cell the active element must at least be the cells submit button + if (!isAddCell && !submitButtonIsActive) { + return false; + } + + return true; + })(); + + let unsafeAddCells = this._unsafeAddCells(); + + if (unsafeAddCells.length > 0 && !isMassInputSubmit) { + let addButtons = Array.from(unsafeAddCells[0].querySelectorAll('.' + MASS_INPUT_SUBMIT_BUTTON_CLASS)).filter(addButton => addButton.closest('[uw-mass-input]') === this._element); + + if (addButtons.length > 0) { + submitButton = addButtons[0]; + isMassInputSubmit = true; + isAddCell = false; + + this._element.scrollIntoView(); + // this._element.classList.add(MASS_INPUT_SAFETY_SUBMITTED_CLASS); + // this._global.setTimeout(() => { this._element.classList.remove(MASS_INPUT_SAFETY_SUBMITTED_CLASS) }, MASS_INPUT_SAFETY_SUBMITTED_TIMEOUT) + } } - // find the according massinput cell thats hosts the element that triggered the submit - const massInputCell = activeElement.closest(MASS_INPUT_CELL_SELECTOR); - if (!massInputCell) { - return false; - } - - const submitButton = massInputCell.querySelector('.' + MASS_INPUT_SUBMIT_BUTTON_CLASS); - if (!submitButton) { - return false; - } - - const isAddCell = massInputCell.matches(MASS_INPUT_ADD_CELL_SELECTOR); - const submitButtonIsActive = submitButton.matches(':focus, :active'); - // if the cell is not an add cell the active element must at least be the cells submit button - if (!isAddCell && !submitButtonIsActive) { + if (!isMassInputSubmit) { return false; } diff --git a/frontend/src/utils/mass-input/mass-input.sass b/frontend/src/utils/mass-input/mass-input.sass index 89bdf08c9..1339a2253 100644 --- a/frontend/src/utils/mass-input/mass-input.sass +++ b/frontend/src/utils/mass-input/mass-input.sass @@ -1,3 +1,5 @@ +@use "../../app" as * + .massinput-list__wrapper, .massinput-list__cell display: grid grid: auto / auto 50px @@ -12,3 +14,14 @@ .massinput-list__cell grid-column: 1 / 3 + +/* .massinput--safety-submitted +/* animation: massinput--safety-submitted linear 1s + +/* @keyframes massinput--safety-submitted +/* 0% +/* background-color: rgba(252, 153, 0, 0) +/* 50% +/* background-color: rgba(252, 153, 0, 0.8) +/* 100% +/* background-color: rgba(252, 153, 0, 0) diff --git a/frontend/src/utils/show-hide/show-hide.js b/frontend/src/utils/show-hide/show-hide.js index 4da5f8c06..3419ee1f4 100644 --- a/frontend/src/utils/show-hide/show-hide.js +++ b/frontend/src/utils/show-hide/show-hide.js @@ -57,23 +57,44 @@ export class ShowHide { this._element.classList.add(SHOW_HIDE_TOGGLE_RIGHT_CLASS); } + this._checkHash(); + + window.addEventListener('hashchange', this._checkHash.bind(this)); + // mark as initialized this._element.classList.add(SHOW_HIDE_INITIALIZED_CLASS, SHOW_HIDE_TOGGLE_CLASS); } - destroy() { - this._element.removeEventListener('click', this._clickHandler); - } + destroy() {} _addClickListener() { - this._element.addEventListener('click', this._clickHandler); + this._element.addEventListener('click', this._clickHandler.bind(this)); } - _clickHandler = () => { - const newState = this._element.parentElement.classList.toggle(SHOW_HIDE_COLLAPSED_CLASS); + _show() { + this._element.parentElement.classList.remove(SHOW_HIDE_COLLAPSED_CLASS); + } + + _toggle() { + return this._element.parentElement.classList.toggle(SHOW_HIDE_COLLAPSED_CLASS); + } + + _clickHandler(event) { + if (event.target.closest('a') && event.target.closest('a') !== this._element) + return; + if (event.target.matches('a') && event.target !== this._element) + return; + + const newState = this._toggle(); if (this._showHideId) { this._storageManager.save(this._showHideId, newState); } } + + _checkHash() { + if (this._element.id && '#' + this._element.id === location.hash) { + this._show(); + } + } } diff --git a/frontend/src/utils/show-hide/show-hide.sass b/frontend/src/utils/show-hide/show-hide.sass index cb5c922f2..9609ac0a5 100644 --- a/frontend/src/utils/show-hide/show-hide.sass +++ b/frontend/src/utils/show-hide/show-hide.sass @@ -19,7 +19,7 @@ $show-hide-toggle-size: 6px border-right: 2px solid currentColor border-top: 2px solid currentColor transition: transform .2s ease - transform: translateY(-50%) rotate(-45deg) + transform: translateY(2px) translateY(-50%) rotate(-45deg) @media (max-width: 768px) left: auto @@ -33,7 +33,7 @@ $show-hide-toggle-size: 6px .show-hide--collapsed .show-hide__toggle::before - transform: translateY(-50%) rotate(135deg) + transform: translateY(-2px) translateY(-50%) rotate(135deg) & > :not(.show-hide__toggle) display: block diff --git a/ghci.sh b/ghci.sh index 441f9f649..ab5479c78 100755 --- a/ghci.sh +++ b/ghci.sh @@ -4,10 +4,16 @@ set -e [ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en .stack-work.lock "$0" "$@" || : +export DETAILED_LOGGING=${DETAILED_LOGGING:-true} +export LOG_ALL=${LOG_ALL:-false} +export LOGLEVEL=${LOGLEVEL:-info} +export DUMMY_LOGIN=${DUMMY_LOGIN:-true} +export SERVER_SESSION_ACID_FALLBACK=${SERVER_SESSION_ACID_FALLBACK:-true} +export SERVER_SESSION_COOKIES_SECURE=${SERVER_SESSION_COOKIES_SECURE:-false} +export ALLOW_DEPRECATED=${ALLOW_DEPRECATED:-true} +export RIBBON=${RIBBON:-${__HOST:-localhost}} unset HOST -export DETAILED_LOGGING=true -export LOG_ALL=true -export DUMMY_LOGIN=true + move-back() { mv -v .stack-work .stack-work-ghci diff --git a/messages/faq/de-de-formal.msg b/messages/faq/de-de-formal.msg new file mode 100644 index 000000000..67db65216 --- /dev/null +++ b/messages/faq/de-de-formal.msg @@ -0,0 +1,5 @@ +FAQNoCampusAccount: Ich habe keine LMU-Benutzerkennung (ehem. Campus-Kennung); kann ich trotzdem Zugang zum System erhalten? +FAQForgottenPassword: Ich habe mein Passwort vergessen +FAQCampusCantLogin: Ich kann mich mit meiner LMU-Benutzerkennung (ehem. Campus-Kennung) nicht anmelden +FAQCourseCorrectorsTutors: Wie kann ich Tutoren oder Korrektoren für meinen Kurs einstellen? +FAQNotLecturerHowToCreateCourses: Wie kann ich einen neuen Kurs anlegen? \ No newline at end of file diff --git a/messages/faq/en-eu.msg b/messages/faq/en-eu.msg new file mode 100644 index 000000000..7c0c3d80e --- /dev/null +++ b/messages/faq/en-eu.msg @@ -0,0 +1,5 @@ +FAQNoCampusAccount: I don't have a LMU user ID (formerly Campus-ID); can I still get access to Uni2work? +FAQForgottenPassword: I have forgotten my password +FAQCampusCantLogin: I can't log in using my LMU user ID (formerly Campus-ID) +FAQCourseCorrectorsTutors: How can I add tutors or correctors to my course? +FAQNotLecturerHowToCreateCourses: How can I create new courses? diff --git a/messages/uniworx/de-de-formal.msg b/messages/uniworx/de-de-formal.msg index a89735757..50b3ea753 100644 --- a/messages/uniworx/de-de-formal.msg +++ b/messages/uniworx/de-de-formal.msg @@ -32,6 +32,10 @@ BtnLecInvDecline: Ablehnen BtnCorrInvAccept: Annehmen BtnCorrInvDecline: Ablehnen BtnSubmissionsAssign: Abgaben automatisch zuteilen +BtnAllocationCompute: Vergabe berechnen +BtnAllocationAccept: Vergabe akzeptieren +BtnSystemMessageHide: Verstecken +BtnSystemMessageUnhide: Nicht mehr verstecken Aborted: Abgebrochen @@ -116,11 +120,12 @@ CourseStudyFeatureTip: Dient ausschließlich der Information der Kursverwalter CourseStudyFeatureUpdated: Assoziiertes Studienfach geändert CourseStudyFeatureNone: Kein assoziiertes Studienfach CourseTutorial: Tutorium +CourseExam: Prüfung CourseSecretWrong: Falsches Passwort CourseSecret: Zugangspasswort CourseEditOk tid@TermId ssh@SchoolId csh@CourseShorthand: Kurs #{tid}-#{ssh}-#{csh} wurde erfolgreich geändert. -CourseNewDupShort tid@TermId ssh@SchoolId csh@CourseShorthand: Kurs #{tid}-#{ssh}-#{csh} konnte nicht erstellt werden: Es gibt bereits einen anderen Kurs mit dem Kürzel #{csh} in diesem Semester und Institut. -CourseEditDupShort tid@TermId ssh@SchoolId csh@CourseShorthand: Kurs #{tid}-#{ssh}-#{csh} konnte nicht geändert werden: Es gibt bereits einen anderen Kurs mit dem Kürzel #{csh} in diesem Semester und Institut. +CourseNewDupShort tid@TermId ssh@SchoolId csh@CourseShorthand: Kurs #{tid}-#{ssh}-#{csh} konnte nicht erstellt werden: Es gibt bereits einen anderen Kurs mit dem selben Kürzel oder Titel in diesem Semester und Institut. +CourseEditDupShort tid@TermId ssh@SchoolId csh@CourseShorthand: Kurs #{tid}-#{ssh}-#{csh} konnte nicht geändert werden: Es gibt bereits einen anderen Kurs mit dem selben Kürzel oder Titel in diesem Semester und Institut. FFSheetName: Name TermCourseListHeading tid@TermId: Kursübersicht #{tid} TermSchoolCourseListHeading tid@TermId school@SchoolName: Kursübersicht #{tid} für #{school} @@ -136,7 +141,7 @@ CourseAssociatedWith: assoziiert mit CourseMembersCount n@Int: #{n} CourseMembersCountLimited n@Int max@Int: #{n}/#{max} CourseMembersCountOf n@Int mbNum@IntMaybe: #{n} Kursanmeldungen #{maybeToMessage " von " mbNum " möglichen"} -CourseName: Name +CourseName: Kurstitel CourseDescription: Beschreibung CourseHomepageExternal: Externe Homepage CourseShorthand: Kürzel @@ -157,7 +162,13 @@ CourseFilterNone: — BoolIrrelevant: — CourseDeleteQuestion: Wollen Sie den unten aufgeführten Kurs wirklich löschen? CourseDeleted: Kurs gelöscht +CourseUserTutorial: Angemeldetes Tutorium CourseUserTutorials: Angemeldete Tutorien +CourseUserExam: Angemeldete Prüfung +CourseUserExams: Angemeldete Prüfungen +CourseSingleUserExams: Prüfungen +CourseSingleUserTutorials: Tutorien +CourseUserCorrections: Abgaben CourseUserNote: Notiz CourseUserNoteTooltip: Nur für Verwalter dieses Kurses einsehbar CourseUserNoteSaved: Notizänderungen gespeichert @@ -166,7 +177,9 @@ CourseUserRegister: Zum Kurs anmelden CourseUserDeregister: Vom Kurs abmelden CourseUsersDeregistered count@Int64: #{show count} Teilnehmer vom Kurs abgemeldet CourseUserRegisterTutorial: Zu einem Tutorium anmelden +CourseUserRegisterExam: Zu einer Prüfung anmelden CourseUsersTutorialRegistered count@Int64: #{show count} Teilnehmer zum Tutorium angemeldet +CourseUsersExamRegistered count@Int64: #{show count} Teilnehmer zur Prüfung angemeldet CourseUserSendMail: Mitteilung verschicken TutorialUserDeregister: Vom Tutorium Abmelden TutorialUserSendMail: Mitteilung verschicken @@ -311,15 +324,19 @@ SheetSolutionFromTip: Ohne Datum nie für Teilnehmer sichtbar, Korrektoren könn SheetMarkingTip: Hinweise zur Korrektur, sichtbar nur für Korrektoren SheetPseudonym: Persönliches Abgabe-Pseudonym SheetGeneratePseudonym: Generieren +SheetAnonymousCorrection: Anonymisierte Korrektur +SheetAnonymousCorrectionTip: Wenn die Korrektur anonymisiert erfolgt, können Korrektoren die ihnen zugeteilten Abgaben nicht bestimmten Studierenden zuordnen (Name, Matrikelnummer und feste Abgabegruppe der Abgebenden werden versteckt) SheetFormType: Wertung & Abgabe SheetFormTimes: Zeiten SheetFormFiles: Dateien -SheetErrVisibility: "Beginn Abgabezeitraum" muss nach "Sichbar für Teilnehmer ab" liegen -SheetErrDeadlineEarly: "Ende Abgabezeitraum" muss nach "Beginn Abzeitraum" liegen +SheetErrVisibility: "Aktiv ab/Beginn Abgabezeitraum" muss nach "Sichbar für Teilnehmer ab" liegen +SheetErrDeadlineEarly: "Aktiv bis/Ende Abgabezeitraum" muss nach "Aktiv ab/Beginn Abzeitraum" liegen SheetErrHintEarly: Hinweise dürfen erst nach Beginn des Abgabezeitraums herausgegeben werden SheetErrSolutionEarly: Lösungen dürfen erst nach Ende der Abgabezeitraums herausgegeben werden +SheetErrVisibleWithoutActive: Wird "Sichtbar für Teilnehmer ab" angegeben, muss auch "Aktiv ab/Beginn Abgabezeitraum" angegeben werden +SheetWarnNoActiveTo: "Aktiv bis/Ende Abgabezeitraum" sollte stets angegeben werden SheetNoCurrent: Es gibt momentan kein aktives Übungsblatt. SheetNoOldUnassigned: Alle Abgaben inaktiver Blätter sind bereits einen Korrektor zugeteilt. SheetsUnassignable name@Text: Momentan keine Abgaben zuteilbar für #{name} @@ -395,6 +412,7 @@ UnauthorizedTokenExpired: Ihr Authorisierungs-Token ist abgelaufen. UnauthorizedTokenNotStarted: Ihr Authorisierungs-Token ist noch nicht gültig. UnauthorizedTokenInvalid: Ihr Authorisierungs-Token konnte nicht verarbeitet werden. UnauthorizedTokenInvalidRoute: Ihr Authorisierungs-Token ist auf dieser Unterseite nicht gültig. +UnauthorizedTokenInvalidNoAuthority: Ihr Authorisierungs-Token nennt keine Nutzer, auf deren Rechten es basiert. UnauthorizedTokenInvalidAuthority: Ihr Authorisierungs-Token basiert auf den Rechten eines Nutzers, der nicht mehr existiert. UnauthorizedTokenInvalidAuthorityGroup: Ihr Authorisierungs-Token basiert auf den Rechten einer Gruppe von Nutzern, die nicht mehr existiert. UnauthorizedTokenInvalidAuthorityValue: Ihr Authorisierungs-Token basiert auf Rechten, deren Spezifikation nicht interpretiert werden konnte. @@ -458,6 +476,8 @@ UnauthorizedLDAP: Angegebener Nutzer meldet sich nicht mit Campus-Kennung an. UnauthorizedPWHash: Angegebener Nutzer meldet sich nicht mit Uni2work-Kennung an. UnauthorizedExternalExamListNotEmpty: Liste von externen Prüfungen ist nicht leer UnauthorizedExternalExamLecturer: Sie sind nicht als Prüfer für diese externe Prüfung eingetragen +UnauthorizedSubmissionSubmissionGroup: Sie sind nicht Mitglied in einer der registrierten Abgabegruppen, die an dieser Abgabe beteiligt sind +UnauthorizedSheetSubmissionGroup: Sie sind nicht Mitglied in einer registrierten Abgabegruppe UnauthorizedPasswordResetToken: Dieses Authorisierungs-Token kann nicht mehr zum Passwort ändern benutzt werden @@ -514,11 +534,17 @@ NewsOpenAllocations: Offene Zentralanmeldungen NewsUpcomingSheets: Anstehende Übungsblätter NewsUpcomingExams: Bevorstehende Prüfungen +NewsHideHiddenSystemMessages: Versteckte Nachrichten nicht mehr anzeigen +NewsShowHiddenSystemMessages: Versteckte Nachrichten anzeigen + NumCourses num@Int64: #{num} #{pluralDE num "Kurs" "Kurse"} CloseAlert: Schliessen Name: Name MatrikelNr: Matrikelnummer +Surname: Nachname(n) +FirstName: Vorname(n) +Title: Titel LdapSynced: LDAP-Synchronisiert LdapSyncedBefore: Letzte LDAP-Synchronisation vor NoMatrikelKnown: Keine Matrikelnummer @@ -545,6 +571,12 @@ DBTablePagesize: Einträge pro Seite DBTablePagesizeAll: Alle CorrDownload: Herunterladen +CorrDownloadAnonymous: Anonymisiert +CorrDownloadAnonymousTip: Wenn Abgaben nicht-anonymisiert heruntergeladen werden, werden an die Verzeichnisnamen der einzelnen Abgaben das ausgewählte Merkmal der Abgeber angehängt, sofern erlaubt +SubmissionDownloadAnonymous: Anonymisiert +SubmissionDownloadSurnames: Mit Nachnamen +SubmissionDownloadMatriculations: Mit Matrikelnummern +SubmissionDownloadGroups: Mit festen Abgabegruppen CorrUploadField: Korrekturen CorrUpload: Korrekturen hochladen CorrSetCorrector: Korrektor zuweisen @@ -747,7 +779,7 @@ UploadModeAny: Upload, beliebige Datei(en) UploadModeSpecific: Upload, vorgegebene Dateinamen UploadModeUnpackZips: Abgabe mehrerer Dateien -UploadModeUnpackZipsTip: Wenn die Abgabe mehrerer Dateien erlaubt ist, werden auch unterstützte Archiv-Formate zugelassen. Diese werden nach dann beim Hochladen automatisch entpackt. +UploadModeUnpackZipsTip: Wenn die Abgabe mehrerer Dateien erlaubt ist, werden auch unterstützte Archiv-Formate zugelassen. Diese werden dann beim Hochladen automatisch entpackt. AutoUnzip: ZIPs automatisch entpacken AutoUnzipInfo: Entpackt hochgeladene ZIP-Dateien (*.zip) automatisch und fügt den Inhalt dem Stamm-Verzeichnis hinzu. @@ -756,6 +788,9 @@ UploadModeExtensionRestriction: Zulässige Dateiendungen UploadModeExtensionRestrictionTip: Komma-separiert. Wenn keine Dateiendungen angegeben werden erfolgt keine Einschränkung. UploadModeExtensionRestrictionEmpty: Liste von zulässigen Dateiendungen darf nicht leer sein +GenericFileFieldInvalidExtension file@FilePath: „#{file}” hat keine zulässige Dateiendung +FileUploadOnlySessionTip: Sie haben diese Datei in der aktuellen Session bereits hochgeladen, sie ist allerdings noch nicht gespeichert. Sie müssen zunächst noch das Formular „Senden“, damit die Datei ordnungsgemäß gespeichert wird. + UploadSpecificFiles: Vorgegebene Dateinamen NoUploadSpecificFilesConfigured: Wenn der Abgabemodus vorgegebene Dateinamen vorsieht, muss mindestens ein vorgegebener Dateiname konfiguriert werden. UploadSpecificFilesDuplicateNames: Vorgegebene Dateinamen müssen eindeutig sein @@ -769,6 +804,8 @@ CorrectorSubmissions: Abgabe extern mit Pseudonym UserSubmissions: Direkte Abgabe in Uni2work BothSubmissions: Abgabe direkt in Uni2work & extern mit Pseudonym +BothSubmissionsTip: Abgabe kann, nach Wahl des Teilnehmers, entweder direkt in Uni2work oder extern mit Pseudonym erfolgen + SheetCorrectorSubmissionsTip: Abgabe erfolgt über ein Uni2work-externes Verfahren (zumeist in Papierform durch Einwurf) unter Angabe eines persönlichen Pseudonyms. Korrektoren können mithilfe des Pseudonyms später Korrekturergebnisse in Uni2work eintragen, damit Sie sie einsehen können. SubmissionNoUploadExpected: Es ist keine Abgabe von Dateien vorgesehen. @@ -947,8 +984,9 @@ SheetGradingPassBinary': Bestanden/Nicht bestanden SheetTypeBonus grading@SheetGrading: Bonus SheetTypeNormal grading@SheetGrading: Normal -SheetTypeInformational grading@SheetGrading: Ohne Anrechung +SheetTypeInformational grading@SheetGrading: Ohne Anrechnung SheetTypeNotGraded: Keine Korrektur +SheetTypeInfoNormalLecturer: Normale Blätter werden zur Berechnung eines etwaigen Klausurbonus herangezogen. Der Bonus kann sowohl anhand der zu bestehenden Blätter als auch der erreichbaren Maximalpunktzahl automatisch oder manuell berechnet werden. SheetTypeInfoNotGraded: Keine Korrektur bedeutet, dass es gar kein Feedback gibt. SheetTypeInfoBonus: Bonus Blätter zählen normal, erhöhen aber nicht die maximal erreichbare Punktzahl bzw. Anzahl zu bestehender Blätter. SheetTypeInfoInformational: Blätter ohne Anrechnung werden nirgends angerechnet, die Bewertung durch den Korrektor dient lediglich zur Information der Teilnehmer. @@ -1051,10 +1089,23 @@ HelpRequest: Supportanfrage / Verbesserungsvorschlag HelpProblemPage: Problematische Seite HelpIntroduction: Wenn Ihnen die Benutzung dieser Webseite Schwierigkeiten bereitet oder Sie einen verbesserbaren Umstand entdecken bitten wir Sie uns das zu melden, auch wenn Sie Ihr Problem bereits selbst lösen konnten. Wir passen die Seite ständig an und versuchen sie auch für zukünftige Benutzer so einsichtig wie möglich zu halten. HelpSent: Ihre Supportanfrage wurde weitergeleitet. +HelpSendLastError: Letzte Fehlermeldung anhängen +HelpError: Letzte Fehlermeldung +HelpErrorYamlFilename mailId@MailObjectId: fehlermeldung-#{toPathPiece mailId}.yaml +HelpErrorOrRequestRequired: Bitte geben Sie entweder eine Supportanfrage bzw. einen Verbesserungsvorschlag an oder hängen Sie die letzte Fehlermeldung an InfoLecturerTitle: Hinweise für Veranstalter +SystemMessageNewsOnly: Nur auf "Aktuelles" +SystemMessageRecordChanged: Signifikante Änderung +SystemMessageRecordChangedTip: Soll der "zuletzt geändert"-Zeitstempel gesetzt werden? Nachrichten werden auf "Aktuelles" danach sortiert und bei signifikanten Änderungen erneut als Benachrichtigung unten rechts angezeigt. +SystemMessageUnhide: "Verstecken" ignorieren +SystemMessageUnhideTip: Soll die Nachricht für Benutzer, die sie aktiv versteckt haben, erneut angezeigt werden? +SystemMessageCreated: Erstellt +SystemMessageLastChanged: Zuletzt geändert +SystemMessageLastChangedAt time@Text: Zuletzt geändert: #{time} +SystemMessageLastUnhide: Zuletzt un-versteckt SystemMessageFrom: Sichtbar ab SystemMessageTo: Sichtbar bis SystemMessageAuthenticatedOnly: Nur angemeldet @@ -1147,6 +1198,7 @@ MenuUserPassword: Passwort MenuAdminTest: Admin-Demo MenuMessageList: Systemnachrichten MenuAdminErrMsg: Fehlermeldung entschlüsseln +MenuAdminTokens: Tokens ausstellen MenuProfileData: Persönliche Daten MenuTermCreate: Neues Semester anlegen MenuCourseNew: Neuen Kurs anlegen @@ -1214,6 +1266,9 @@ MenuParticipantsList: Kursteilnehmerlisten MenuParticipantsIntersect: Überschneidung von Kursteilnehmern MenuAllocationUsers: Bewerber MenuAllocationPriorities: Zentrale Dringlichkeiten +MenuAllocationCompute: Platzvergabe berechnen +MenuAllocationAccept: Platzvergabe akzeptieren +MenuFaq: FAQ BreadcrumbSubmissionFile: Datei BreadcrumbSubmissionUserInvite: Einladung zur Abgabe @@ -1281,6 +1336,10 @@ BreadcrumbExamAutoOccurrence: Automatische Termin-/Raumverteilung BreadcrumbStorageKey: Lokalen Schlüssel generieren BreadcrumbAllocationUsers: Bewerber BreadcrumbAllocationPriorities: Zentrale Dringlichkeiten +BreadcrumbAllocationCompute: Platzvergabe berechnen +BreadcrumbAllocationAccept: Platzvergabe akzeptieren +BreadcrumbMessageHide: Verstecken +BreadcrumbFaq: FAQ ExternalExamEdit coursen@CourseName examn@ExamName: Bearbeiten: #{coursen}, #{examn} ExternalExamGrades coursen@CourseName examn@ExamName: Prüfungsleistungen: #{coursen}, #{examn} @@ -1331,6 +1390,7 @@ AuthTagIsPWHash: Nutzer meldet sich mit Uni2work-Kennung an AuthTagAuthentication: Nutzer ist angemeldet, falls erforderlich AuthTagRead: Zugriff ist nur lesend AuthTagWrite: Zugriff ist i.A. schreibend +AuthTagSubmissionGroup: Nutzer ist Mitglied in registrierter Abgabegruppe DeleteCopyStringIfSure n@Int: Wenn Sie sich sicher sind, dass Sie #{pluralDE n "das obige Objekt" "obige Objekte"} unwiderbringlich löschen möchten, schreiben Sie bitte zunächst den angezeigten Text ab. DeletePressButtonIfSure n@Int: Wenn Sie sich sicher sind, dass Sie #{pluralDE n "das obige Objekt" "obige Objekte"} unwiderbringlich löschen möchten, bestätigen Sie dies bitte durch Drücken des untigen Knopfes. @@ -1460,7 +1520,7 @@ ExceptionKindNoOccur: Findet nicht statt ExceptionExists: Diese Ausnahme existiert bereits ExceptionNoOccurAt: Termin -TutorialType: Typ +TutorialType: Art TutorialTypePlaceholder: Tutorium, Zentralübung, ... TutorialTypeTip: Dient nur der Information der Studierenden TutorialName: Bezeichnung @@ -1681,6 +1741,7 @@ ExamRegistered: Zur Prüfung angemeldet ExamNotRegistered: Nicht zur Prüfung angemeldet ExamRegistration: Prüfungsanmeldung ExamLoginToRegister: Um sich zum Kurs anzumelden müssen Sie zunächst in Uni2work anmelden +ExamRegistrationTime: Angemeldet seit ExamRegisterToMustBeAfterRegisterFrom: "Anmeldung ab" muss vor "Anmeldung bis" liegen ExamDeregisterUntilMustBeAfterRegisterFrom: "Abmeldung bis" muss nach "Anmeldung bis" liegen @@ -1720,6 +1781,20 @@ ExamUsersResultsReset count@Int64: Prüfungsergebnis für #{show count} Teilnehm ExamUsersPartResultsSet count@Int64: Teilprüfungsergebnis für #{show count} Teilnehmer angepasst ExamUsersBonusSet count@Int64: Bonuspunkte für #{show count} Teilnehmer angepasst ExamUsersResultSet count@Int64: Prüfungsergebnis für #{show count} Teilnehmer angepasst +CourseUserTutorialsDeregistered count@Int64: Teilnehmer von #{show count} #{pluralDE count "Tutorium" "Tutorien"} abgemeldet +CourseUserNoTutorialsDeregistered: Teilnehmer ist zu keinem der gewählten Tutorien angemeldet +CourseUserExamsDeregistered count@Int64: Teilnehmer von #{show count} #{pluralDE count "Prüfung" "Prüfungen"} abgemeldet +CourseUserNoExamsDeregistered: Teilnehmer ist zu keiner der gewählten Prüfungen angemeldet +CourseUserExamsResultSet count@Int64: Ergebnis zu #{show count} #{pluralDE count "Prüfung" "Prüfungen"} erfolgreich angepasst +CourseUserExamResultDoesNotMatchMode examn@ExamName: Gewähtes Ergebnis passt nicht zu Bewertungsmodus von Prüfung „#{examn}“. +CourseUserSetSubmissionGroup: Feste Abgabegruppe setzen/entfernen +CourseUsersSubmissionGroupSetNew count@Int64: #{show count} Benutzer der festen Abgabegruppe zugeordnet +CourseUsersSubmissionGroupUnset count@Int64: #{show count} Benutzer aus ihren jeweiligen festen Abgabegruppen entfernt +CourseUsersStateSet count@Int64: Zustand von #{show count} #{pluralDE count "Benutzer" "Benutzern"} angepasst + +SubmissionGroup: Feste Abgabegruppe +NoSubmissionGroup: Keine feste Abgabegruppe +SubmissionGroupEmptyIsUnsetTip: Leer lassen um Benutzer aus den jeweiligen Abgabegruppen ersatzlos zu entfernen ExamUserSynchronised: Synchronisiert ExamUserSyncOfficeName: Name @@ -1758,6 +1833,7 @@ CsvDeleteMissing: Fehlende Einträge entfernen BtnCsvExport: CSV-Datei exportieren BtnCsvImport: CSV-Datei importieren BtnCsvImportConfirm: CSV-Import abschließen +BtnCsvImportAbort: Abbrechen CsvImportNotConfigured: CSV-Import nicht vorgesehen CsvImportConfirmationHeading: CSV-Import Vorschau (noch keine Änderungen importiert) @@ -1766,6 +1842,8 @@ CsvImportUnnecessary: Durch den CSV-Import würden keine Änderungen vorgenommen CsvImportSuccessful n@Int: CSV-Import erfolgreich, es #{pluralDE n "wurde eine Aktion" (mappend (mappend "wurden " (toMessage n)) " Aktionen")} durchgeführt CsvImportAborted: CSV-Import abgebrochen CsvImportExplanationLabel: Hinweise zum CSV-Import +CsvExampleData: Beispiel-Datei +CsvExportExample: Beispiel-CSV exportieren Proportion c@Text of@Text prop@Rational: #{c}/#{of} (#{rationalToFixed2 (100 * prop)}%) ProportionNoRatio c@Text of@Text: #{c}/#{of} @@ -1806,6 +1884,8 @@ CsvColumnUserSemester: Fachsemester des Teilnehmers im assoziierten Studienfach CsvColumnUserRegistration: Zeitpunkt der Anmeldung zum Kurs (ISO 8601) CsvColumnUserNote: Notizen zum Teilnehmer CsvColumnUserTutorial: Tutorien zu denen der Teilnehmer angemeldet ist, als Semikolon (;) separierte Liste. Für Registrierungs-Gruppen unter den Tutorien gibt es jeweils eine weitere Spalte. Die Registrierungs-Gruppen-Spalten enthalten jeweils maximal ein Tutorium pro Teilnehmer. Sind alle Tutorien in Registrierungs-Gruppen, so gibt es keine Spalte "tutorial". +CsvColumnUserExam: Prüfungen zu denen der Teilnehmer angemeldet ist, als Semikolon (;) separierte Liste. +CsvColumnUserSubmissionGroup: Registrierte Abgabegruppe CsvColumnExamOfficeExamUserOccurrenceStart: Prüfungstermin (ISO 8601) @@ -1829,6 +1909,8 @@ DBCsvDuplicateKey: Zwei Zeilen der CSV-Dateien referenzieren den selben internen DBCsvDuplicateKeyTip: Entfernen Sie eine der unten aufgeführten Zeilen aus Ihren CSV-Dateien und versuchen Sie es erneut. DBCsvKeyException: Für eine Zeile der CSV-Dateien konnte nicht festgestellt werden, ob sie zu einem bestehenden internen Datensatz korrespondieren. DBCsvException: Bei der Berechnung der auszuführenden Aktionen für einen Datensatz ist ein Fehler aufgetreten. +DBCsvParseError: Eine hochgeladene Datei konnte nicht korrekt als CSV-Datei im erwarteten Format interpretiert werden. +DBCsvParseErrorTip: Die Uni2work-Komponente, die für das Interpretieren von CSV-Dateien zuständig ist, hat folgende Fehlermeldung produziert: ExamUserCsvCourseRegister: Benutzer zum Kurs und zur Prüfung anmelden ExamUserCsvRegister: Kursteilnehmer zur Prüfung anmelden @@ -1845,10 +1927,11 @@ ExamBonusNone: Keine Bonuspunkte ExamUserCsvCourseNoteDeleted: Notiz wird gelöscht -ExamUserCsvExceptionNoMatchingUser: Kursteilnehmer konnte nicht eindeutig identifiziert werden -ExamUserCsvExceptionNoMatchingStudyFeatures: Das angegebene Studienfach konnte keinem Studienfach des Kursteilnehmers zugeordnet werden -ExamUserCsvExceptionNoMatchingOccurrence: Raum/Termin konnte nicht eindeutig identifiziert werden +ExamUserCsvExceptionNoMatchingUser: Benutzer konnte nicht eindeutig identifiziert werden. Alle Identifikatoren des Benutzers (Vorname(n), Nachname, Voller Name, Matrikelnummer, ...) müssen exakt übereinstimmen. Sie können versuchen für diese Zeile manche der Identifikatoren zu entfernen (also z.B. nur eine Matrikelnummer angeben) um dem System zu erlauben nur Anhand der verbleibenden Identifikatoren zu suchen. Sie sollten dann natürlich besonders kontrollieren, dass das System den fraglichen Benutzer korrekt identifiziert hat. +ExamUserCsvExceptionNoMatchingStudyFeatures: Das angegebene Studienfach konnte keinem Studienfach des Benutzers zugeordnet werden. Sie können versuchen für diese Zeile die Studiengangsdaten zu entfernen um das System automatisch ein Studienfach wählen zu lassen. +ExamUserCsvExceptionNoMatchingOccurrence: Raum/Termin konnte nicht eindeutig identifiziert werden. Überprüfen Sie, dass diese Zeile nur interne Raumbezeichnungen enthält, wie sie auch für die Prüfung konfiguriert wurden. ExamUserCsvExceptionMismatchedGradingMode expectedGradingMode@ExamGradingMode actualGradingMode@ExamGradingMode: Es wurde versucht eine Prüfungsleistung einzutragen, die zwar vom System interpretiert werden konnte, aber nicht dem für diese Prüfung erwarteten Modus entspricht. Der erwartete Bewertungsmodus kann unter "Prüfung bearbeiten" angepasst werden ("Bestanden/Nicht Bestanden", "Numerische Noten" oder "Gemischt"). +ExamUserCsvExceptionNoOccurrenceTime: Es wurde versucht eine Prüfungsleistung ohne einen zugehörigen Zeitpunkt einzutragen. Sie können entweder einen Zeitpunkt pro Student in der entsprechenden Spalte hinterlegen, oder einen voreingestellten Zeitpunkt unter "Bearbeiten" angeben. ExternalExamUserCsvRegister: Prüfungsleistung hinterlegen ExternalExamUserCsvSetTime: Zeitpunkt anpassen @@ -2179,8 +2262,13 @@ CourseNewsDeleteQuestion: Wollen Sie die unten aufgeführte Nachricht wirklich l CourseNewsDeleted: Kursnachricht erfolgreich gelöscht CourseDeregistrationAllocationLog: Ihr Platz in diesem Kurs stammt aus einer Zentralanmeldung. Wenn Sie sich vom Kurs abmelden wird dieser Umstand permanent im System gespeichert und kann Sie u.U. bei zukünftigen Zentralanmeldungen benachteiligen. Wenn Sie gute Gründe vorzuweisen haben, warum Ihre Abmeldung nicht selbstverschuldet ist, kontaktieren Sie bitte einen Kursverwalter. Diese haben die Möglichkeit Sie ohne permanente Eintragung im System abzumelden. +CourseDeregistrationNoShow: Wenn Sie sich vom Kurs abmelden, wird für alle Prüfungen des Kurses „nicht erschienen“ gemeldet. Wenn Sie gute Gründe vorzuweisen haben, warum Ihre Abmeldung nicht selbstverschuldet ist, kontaktieren Sie bitte einen Kursverwalter. Diese haben die Möglichkeit Sie ohne permanente Eintragung im System abzumelden. CourseDeregistrationAllocationReason: Grund CourseDeregistrationAllocationReasonTip: Der angegebene Grund wird permanent im System hinterlegt und ist i.A. einziger Anhaltspunkt zur Schlichtung etwaiger Konflikte +CourseDeregistrationAllocationNoShow: „Nicht erschienen“ eintragen +CourseDeregistrationAllocationNoShowTip: Soll für alle Prüfungen dieses Kurses „nicht erschienen“ als Prüfungsleistung eingetragen werden? Dies geschieht einmalig bei der Abmeldung (sofern nicht bereits eine Prüfungsleistung existiert) und automatisch beim Anlegen von neuen Prüfungen. +CourseDeregisterNoShow: „Nicht erschienen“ bei Abmeldung +CourseDeregisterNoShowTip: Soll, wenn sich Teilnehmer selbstständig abmelden, für alle Prüfungen dieses Kurses „nicht erschienen“ als Prüfungsleistung eingetragen werden? Dies geschieht einmalig bei der Abmeldung (sofern nicht bereits eine Prüfungsleistung existiert) und automatisch beim Anlegen von neuen Prüfungen. CourseDeregistrationAllocationShouldLog: Selbstverschuldet CourseDeregistrationAllocationShouldLogTip: Falls der Platz des Studierenden, der abgemeldet wird, aus einer Zentralanmeldung stammt, ist vorgesehen einen permanenten Eintrag im System zu speichern, der den Studierenden u.U. bei zukünftigen Zentralanmeldungen benachteiligt. Als Kursverwalter haben Sie die Möglichkeit dies zu unterbinden, wenn der Studierende gute Gründe vorweisen kann, warum seine Abmeldung nicht selbstverschuldet ist. @@ -2202,11 +2290,14 @@ FavouriteParticipant: Ihre Kurse FavouriteManual: Favoriten FavouriteCurrent: Aktueller Kurs +FavouritesUnavailableTip: Das Schnellzugriffsmenü für diesen Kurs ist aktuell nicht verfügbar. + CourseEvents: Termine CourseEventType: Art CourseEventTypePlaceholder: Vorlesung, Zentralübung, ... CourseEventTime: Zeit CourseEventRoom: Regulärer Raum +CourseEventNote: Notiz CourseEventActions: Aktionen CourseEventsActionEdit: Bearbeiten CourseEventsActionDelete: Löschen @@ -2379,3 +2470,70 @@ AllocationPrioritiesFile: CSV-Datei AllocationPrioritiesSunk num@Int64: Zentrale Prioritäten für #{num} Bewerber erfolgreich hinterlegt AllocationPrioritiesMissing num@Int64: Für #{num} Bewerber ist keine zentrale Priorität hinterlegt, da in der hochgeladenen CSV-Datei die #{pluralDE num "entsprechende Matrikelnummer" "entsprechenden Matrikelnummern"} nicht gefunden #{pluralDE num "wurde" "wurden"} AllocationMissingPrioritiesIgnored: Bewerber, für die keine zentrale Priorität angegeben wird, werden bei der Vergabe ignoriert! + +ExampleUser1FirstName: Max ZweiterName +ExampleUser1Surname: Mustermann +ExampleUser1DisplayName: Max Mustermann +ExampleUser2FirstName: Martha +ExampleUser2Surname: Musterstudent +ExampleUser2DisplayName: Musterstudent Martha +ExampleUser3FirstName: Maria +ExampleUser3Surname: Beispiel +ExampleUser3DisplayName: Beispiel + +AllocationUsersMissingPriorities: Teilnehmer ohne zentrale Dringlichkeit +AllocationUsersMissingPrioritiesTip: Es muss sichergestellt sein, dass keine Teilnehmer unberechtigt aus der Zentralvergabe ausgeschlossen werden, indem ihnen keine zentrale Dringlichkeit zugewiesen wurde. +AllocationUsersMissingPrioritiesOk: Es wurde sichergestellt, dass es für jeden der genannten Benutzer einen zulässigen Grund gibt, warum dieser nicht an der Zentralanmeldung teilnehmen sollte. +AllocationRestrictCourses: Kurse einschränken +AllocationRestrictCoursesTip: Sollen nur Plätze für eine Teilmenge von Kursen zugewiesen werden? So können u.A. Nachrücker verteilt werden. Diese Funktionalität sollte nur verwendet werden, wenn manche Kurse aus zulässigen Gründen ausgeschlossen werden müssen; z.B. weil ein Seminar bereits ein Treffen zur Organisation hatte und nun keine weiteren Teilnehmer mehr akzeptieren kann. +AllocationRestrictCoursesSelection: Kurse +AllocationRestrictCoursesSelectionTip: Teilnehmer werden nur auf die Kurse verteilt, die hier angegeben werden. +AllocationUsersMissingPrioritiesNotOk: Zentralvergabe kann nicht erfolgen, solange nicht allen Teilnehmern, die nicht explizit von der Vergabe ausgeschlossen wurden („Teilnehmer ohne zentrale Dringlichkeit”), eine zentrale Dringlichkeit zugewiesen wurde! +AllocationComputed: Eine mögliche Zentralvergabe wurde berechnet und in Ihrer Session gespeichert. Es wurden noch keine Änderungen vorgenommen! +AllocationOnlyCompute: Durch Senden dieses Formulars wird zunächst nur eine mögliche Zentralvergabe berechnet und zur Kontrolle temporär gespeichert. Es werden keine Änderungen am Stand der Datenbank vorgenommen oder Benachrichtigungen verschickt. +AllocationAcceptFormDoesNotMatchSession: Das Formular zum Akzeptieren der Vergabe wurde für ein anderes Vergabeergebnis erzeugt, als aktuell in Ihrer Session gespeichert ist. +ComputedAllocation: Berechnete Vergabe +AllocationAccepted: Zentralvergabe gespeichert. +AllocationMatchedUsers: Neu zugeteilt +AllocationUnmatchedUsers: Teilnehmer ohne zugeteilte Plätze +AllocationUnmatchedCourses: Kurse ohne zugeteilte Teilnehmer +AllocationTime: Zeitpunkt der Vergabe +AllocationRequestedPlaces: Angefragte Plätze +AllocationOfferedPlaces: Angebotene Plätze +AllocationUserNewMatches: Neue Zuteilungen +AllocationUsersCount: Teilnehmer +AllocationCoursesCount: Kurse + +CourseOption tid@TermId ssh@SchoolId coursen@CourseName: #{tid} - #{ssh} - #{coursen} + +BearerTokenUsageWarning: Mit diesem Interface können quesi beliebige Rechte als Tokens kodiert und somit ohne wesentliche weitere Beschränkung frei übertragen werden. Benutzen Sie dieses Interface nur, wenn Sie von einem erfahrenen Entwickler über die Auswirkungen des konkreten Tokens, dass sie ausstellen möchten, beraten wurden! +BearerTokenAuthorityGroups: Token-Authorität (Gruppen) +BearerTokenAuthorityGroupsTip: Die primären Benutzer aller angegebenen Gruppen müssen Zugriff auf eine Route haben, damit das Token den Zugriff auf diese Route erlaubt. +BearerTokenAuthorityUsers: Token-Authorität (Benutzer) +BearerTokenAuthorityUsersTip: Alle angegebenen Benutzer müssen Zugriff auf eine Route haben, damit das Token den Zugriff auf diese Route erlaubt. Der Aussteller muss, bei mit diesem Benutzerinterface erzeugten Tokens, auch Zugriff auf die Route haben (er wird automatisch der Menge von Token-Authoritäten hinzugefügt). +BearerTokenAuthorityUnknownUser email@UserEmail: Ein Nutzer mit E-Mail #{email} ist dem System nicht bekannt +BearerTokenRoutes: Erlaubte Routen +BearerTokenRoutesTip: Wenn die Token-Validität nach Routen eingeschränkt und keine Routen angegeben werden, ist das Token nirgends gültig. +BearerTokenRestrictions: Routen-spezifische Einschränkungen +BearerTokenRestrictRoutes: Token-Validität nach Routen einschränken +BearerTokenAdditionalAuth: Zusätzliche Authorisierung +BearerTokenAdditionalAuthTip: Wird hier nichts angegeben, werden keine Einschränkungen daran gesetzt, wer das Token verwenden kann. Es reicht dann der Besitz. +BearerTokenOverrideExpiration: Ablaufzeitpunkt überschreiben +BearerTokenExpires: Ablaufzeitpunkt +BearerTokenExpiresTip: Wird der Ablaufzeitpunkt überschrieben und kein Ablaufzeitpunkt angegeben, ist das Token für immer gültig. +BearerTokenOverrideStart: Startzeitpunkt +BearerTokenOverrideStartTip: Wird kein Startzeitpunkt angegeben, wird bei Verwendung des Tokens nur der Ablaufzeitpunkt überprüft. + +FaqTitle: Häufig gestellte Fragen +AdditionalFaqs: Weitere häufig gestellte Fragen + +MultiActionUnknownAction: In einem von einem Eingabefeld abhängigen Formular wurde ein Wert gewählt, für den kein Formular verfügbar ist + +CourseParticipantStateIsActive: Aktive Teilnehmer +CourseParticipantStateIsInactive: Ehemalige Teilnehmer +CourseParticipantStateIsActiveFilter: Ansicht +CourseUserReRegister: Wieder anmelden +CourseParticipantActive: Teilnehmer +CourseParticipantInactive: Abgemeldet +CourseParticipantNoShow: Nicht erschienen +CourseUserState: Zustand diff --git a/messages/uniworx/en-eu.msg b/messages/uniworx/en-eu.msg index 06ae2221f..bf7f2964c 100644 --- a/messages/uniworx/en-eu.msg +++ b/messages/uniworx/en-eu.msg @@ -32,6 +32,10 @@ BtnLecInvDecline: Decline BtnCorrInvAccept: Accept BtnCorrInvDecline: Decline BtnSubmissionsAssign: Assign submissions automatically +BtnAllocationCompute: Compute allocation +BtnAllocationAccept: Accept allocation +BtnSystemMessageHide: Hide +BtnSystemMessageUnhide: Unhide Aborted: Aborted @@ -116,11 +120,12 @@ CourseStudyFeatureTip: For information purposes only (visible to course administ CourseStudyFeatureUpdated: Successfully updated associated subject CourseStudyFeatureNone: No associated subject CourseTutorial: Tutorial +CourseExam: Exam CourseSecretWrong: Wrong password CourseSecret: Access password CourseEditOk tid ssh csh: Successfully edited course #{tid}-#{ssh}-#{csh} -CourseNewDupShort tid ssh csh: Could not create course #{tid}-#{ssh}-#{csh}. Another course with shorthand #{csh} already exists for the given semester and school. -CourseEditDupShort tid ssh csh: Could not edit course #{tid}-#{ssh}-#{csh}. Another course with shorthand #{csh} already exists for the given semester and school. +CourseNewDupShort tid ssh csh: Could not create course #{tid}-#{ssh}-#{csh}. Another course with the same shorthand or title already exists for the given semester and school. +CourseEditDupShort tid ssh csh: Could not edit course #{tid}-#{ssh}-#{csh}. Another course with the same shorthand or title already exists for the given semester and school. FFSheetName: Name TermCourseListHeading tid: Courses #{tid} TermSchoolCourseListHeading tid school: Courses #{tid}, #{school} @@ -158,7 +163,13 @@ BoolIrrelevant: — CourseDeleteQuestion: Are you sure you want to delete the below-mentioned course? CourseDeleted: Course deleted CourseUserRegister: Enrol for course +CourseUserTutorial: Registered tutorial CourseUserTutorials: Registered tutorials +CourseUserExam: Registered exam +CourseUserExams: Registered exams +CourseSingleUserExams: Exams +CourseSingleUserTutorials: Tutorials +CourseUserCorrections: Submissions CourseUserNote: Note CourseUserNoteTooltip: Only visible to administrators of this course CourseUserNoteSaved: Successfully saved note changes @@ -167,6 +178,8 @@ CourseUserDeregister: Deregister from course CourseUsersDeregistered count: Successfully deregistered #{show count} users from course CourseUserRegisterTutorial: Register for a tutorial CourseUsersTutorialRegistered count: Successfully registered #{show count} users for tutorial +CourseUserRegisterExam: Register for an exam +CourseUsersExamRegistered count: Successfully registered #{show count} users for exam CourseUserSendMail: Send mail TutorialUserDeregister: Deregister from tutorial TutorialUserSendMail: Send mail @@ -310,6 +323,8 @@ SheetSolutionFromTip: Always invisible for participants if left empty; corrector SheetMarkingTip: Instructions for correction, visible only to correctors SheetPseudonym: Personal pseudonym SheetGeneratePseudonym: Generate +SheetAnonymousCorrection: Anonymized correction +SheetAnonymousCorrectionTip: If correction is anonymized, correctors cannot see which students are involved in submissions that are assigned to them (names, matriculation numbers, and registered submission groups are hidden) SheetFormType: Valuation & submission SheetFormTimes: Times @@ -319,6 +334,8 @@ SheetErrVisibility: "Submission period start" must be after "Visible from" SheetErrDeadlineEarly: "Submission period end" must be after "Submission period start" SheetErrHintEarly: "Hint from" must be after "Submission period start" SheetErrSolutionEarly: "Solution from" must be after "Submission period end" +SheetErrVisibleWithoutActive: If “Visible from (for participants)” is specified “Active from/Submission period start” must also be specified +SheetWarnNoActiveTo: “Active to/Submission period end” should always be specified SheetNoCurrent: There is no currently active exercise sheet SheetNoOldUnassigned: All submissions for inactive sheets are already assigned to correctors. SheetsUnassignable name: Submission for #{name} may not currently be assigned to correctors. @@ -393,6 +410,7 @@ UnauthorizedTokenExpired: Your authorisation-token is expired. UnauthorizedTokenNotStarted: Your authorisation-token is not yet valid. UnauthorizedTokenInvalid: Your authorisation-token could not be processed. UnauthorizedTokenInvalidRoute: Your authorisation-token is not valid for this page. +UnauthorizedTokenInvalidNoAuthority: Your authorisation-token does not list any users on whose rights it is based. UnauthorizedTokenInvalidAuthority: Your authorisation-token is based in an user's rights who does not exist anymore. UnauthorizedTokenInvalidAuthorityGroup: Your authorisation-token is based in an user groups rights which does not exist anymore. UnauthorizedTokenInvalidAuthorityValue: The specification of the rights in which your authorisation-token is based, could not be interpreted. @@ -402,6 +420,7 @@ UnauthorizedSchoolAdmin: You are no administrator for this department. UnauthorizedAdminEscalation: You aren't an administrator for all departments for which this user is an administrator. UnauthorizedExamOffice: You are not part of an exam office. UnauthorizedEvaluation: You are not charged with course evaluation. +UnauthorizedAllocationAdmin: You are not charged with the administration of central allocations. UnauthorizedExamExamOffice: You are not part of the appropriate exam office for any of the participants of this exam. UnauthorizedExternalExamExamOffice: You are not part of the appropriate exam office for any of the participants of this exam. UnauthorizedSchoolLecturer: You are no lecturer for this department. @@ -455,6 +474,8 @@ UnauthorizedLDAP: Specified user does not log in with their campus account. UnauthorizedPWHash: Specified user does not log in with an Uni2work-account. UnauthorizedExternalExamListNotEmpty: List of external exams is not empty UnauthorizedExternalExamLecturer: You are not an associated person for this external exam +UnauthorizedSubmissionSubmissionGroup: You are not member in any of the submission groups for this submission +UnauthorizedSheetSubmissionGroup: You are not member in any submission group UnauthorizedPasswordResetToken: This authorisation-token may no longer be used to change passwords @@ -511,11 +532,17 @@ NewsOpenAllocations: Active central allocations NewsUpcomingSheets: Upcoming exercise sheets NewsUpcomingExams: Upcoming exams +NewsHideHiddenSystemMessages: Don't show hidden news items +NewsShowHiddenSystemMessages: Show hidden news items + NumCourses num: #{num} #{pluralEN num "course" "courses"} CloseAlert: Close Name: Name MatrikelNr: Matriculation +Surname: Surname(s) +FirstName: Given name(s) +Title: Title LdapSynced: LDAP-synchronised LdapSyncedBefore: Last LDAP-synchronisation before NoMatrikelKnown: No matriculation @@ -542,6 +569,12 @@ DBTablePagesize: Entries per page DBTablePagesizeAll: All CorrDownload: Download +CorrDownloadAnonymous: Anonymized +CorrDownloadAnonymousTip: If submissions are downloaded non-anonymized the selected feature of the submittors are appended to the name of the dirctory for each submission where permitted +SubmissionDownloadAnonymous: Anonymized +SubmissionDownloadSurnames: With surnames +SubmissionDownloadMatriculations: With matriculation numbers +SubmissionDownloadGroups: With registered submission groups CorrUploadField: Corrections CorrUpload: Upload corrections CorrSetCorrector: Assign corrector @@ -752,6 +785,9 @@ UploadModeExtensionRestriction: Allowed file extensions UploadModeExtensionRestrictionTip: Comma-separated. If no file extensions are specified, uploads are not restricted. UploadModeExtensionRestrictionEmpty: List of permitted file extensions may not be emptyy +GenericFileFieldInvalidExtension file: “#{file}” does not have an acceptable file extension +FileUploadOnlySessionTip: You have uploaded this file during your current session. It has not yet been saved permanently. The file will be saved permanently if you “Send” as part of this Form. + UploadSpecificFiles: Pre-defined files NoUploadSpecificFilesConfigured: If pre-defined files are selected, at least one file needs to be configured. UploadSpecificFilesDuplicateNames: Names of pre-defined files must be unique @@ -765,6 +801,8 @@ CorrectorSubmissions: External submission via pseudonym UserSubmissions: Direct submission in Uni2work BothSubmissions: Submission either directly in Uni2work or externally via pseudonym +BothSubmissionsTip: Participants may choose to submit either directly in Uni2work or externally via a pseudonym + SheetCorrectorSubmissionsTip: Submissions are expected to be handed in through some Uni2work-external procedure (usually on paper) marked with your personal pseudonym. Correctors can, using the pseudonym, register the marking in Uni2work for you to review. SubmissionNoUploadExpected: No upload of files expected. @@ -948,6 +986,7 @@ SheetTypeBonus grading: Bonus SheetTypeNormal grading: Normal SheetTypeInformational grading: Informational SheetTypeNotGraded: Not marked +SheetTypeInfoNormalLecturer: Normal sheets are used to calculate exam bonuses. Bonuses may be calculated from the number of sheets that can be passed or the maximum number of points achievable either manually or automatically. SheetTypeInfoNotGraded: "Not marked" means that there will be no feedback at all. SheetTypeInfoBonus: Sheets marked "bonus" count normally but do not increase either the maximum number of points or the count of sheets that can be passed. SheetTypeInfoInformational: Sheets marked "informational" do not counted anywhere. They are marked only as feedback for participants. @@ -1013,6 +1052,7 @@ NotificationTriggerKindEvaluation: For course evaluations NotificationTriggerKindAllocationStaff: For central allocations (lecturers) NotificationTriggerKindAllocationParticipant: For central allocations NotificationTriggerKindSubmissionUser: For participants in an exercise sheet submission +NotificationTriggerKindAllocationAdmin: For administrators of central allocations CorrCreate: Register submissions UnknownPseudonymWord pseudonymWord: Invalid pseudonym-word “#{pseudonymWord}” @@ -1049,9 +1089,22 @@ HelpRequest: Support request / Suggestion HelpProblemPage: Problematic page HelpIntroduction: If you have trouble using this website or if you find something that could be improved, please contact us even if you were already able to solve your problem by yourself! We are continually making changes and try to keep the site as intuitive as possible even for new users. HelpSent: Your support request has been sent. +HelpSendLastError: Attach last error message +HelpError: Last error message +HelpErrorYamlFilename mailId: error-#{toPathPiece mailId}.yaml +HelpErrorOrRequestRequired: Please attach either the last error message or submit a support request or a suggestion InfoLecturerTitle: Information for lecturers +SystemMessageNewsOnly: Only on "News" +SystemMessageRecordChanged: Signifcant change +SystemMessageRecordChangedTip: Should the "last changed"-timestamp be adjusted? News are sorted by "last changed" on "News". After a significant change news items are displayed once again as a popup in the bottom right. +SystemMessageUnhide: Ignore previously hidden +SystemMessageUnhideTip: Should the news item be display again for users that have actively hidden it? +SystemMessageCreated: Created +SystemMessageLastChanged: Last changed +SystemMessageLastChangedAt time: Last changed: #{time} +SystemMessageLastUnhide: Last unhidden SystemMessageFrom: Visible from SystemMessageTo: Visible to SystemMessageAuthenticatedOnly: Only logged in users @@ -1144,6 +1197,7 @@ MenuUserPassword: Password MenuAdminTest: Admin-demo MenuMessageList: System messages MenuAdminErrMsg: Decrypt error message +MenuAdminTokens: Issue tokens MenuProfileData: Personal information MenuTermCreate: Create new semester MenuCourseNew: Create new course @@ -1209,6 +1263,11 @@ MenuExternalExamNew: New external exam MenuExternalExamList: External exams MenuParticipantsList: Lists of course participants MenuParticipantsIntersect: Common course participants +MenuAllocationUsers: Applicants +MenuAllocationPriorities: Central priorities +MenuAllocationCompute: Compute allocation +MenuAllocationAccept: Accept allocation +MenuFaq: FAQ BreadcrumbSubmissionFile: File BreadcrumbSubmissionUserInvite: Invitation to participate in a submission @@ -1274,6 +1333,12 @@ BreadcrumbParticipantsList: Lists of course participants BreadcrumbParticipants: Course participants BreadcrumbExamAutoOccurrence: Automatic occurrence/room distribution BreadcrumbStorageKey: Generate storage key +BreadcrumbAllocationUsers: Applicants +BreadcrumbAllocationPriorities: Central priorities +BreadcrumbAllocationCompute: Compute allocation +BreadcrumbAllocationAccept: Accept allocation +BreadcrumbMessageHide: Hide +BreadcrumbFaq: FAQ ExternalExamEdit coursen examn: Edit: #{coursen}, #{examn} ExternalExamGrades coursen examn: Exam achievements: #{coursen}, #{examn} @@ -1288,6 +1353,7 @@ AuthTagFree: Page is freely accessable AuthTagAdmin: User is administrator AuthTagExamOffice: User is part of an exam office AuthTagEvaluation: User is charged with course evaluation +AuthTagAllocationAdmin: User is charged with administration of central allocations AuthTagToken: User is presenting an authorisation-token AuthTagNoEscalation: User permissions are not being expanded to other departments AuthTagDeprecated: Page is not deprecated @@ -1323,6 +1389,7 @@ AuthTagIsPWHash: User logs in using their Uni2work-internal account AuthTagAuthentication: User is authenticated AuthTagRead: Access is read only AuthTagWrite: Access might write +AuthTagSubmissionGroup: User is part of a submission group DeleteCopyStringIfSure n: If you are sure that you want to permanently delete the #{pluralEN n "object" "objects"} listed below, please copy the shown text. DeletePressButtonIfSure n: If you are sure that you want to permanently delete the #{pluralEN n "object" "objects"} listed below, please confirm the action by pressing the button. @@ -1673,6 +1740,7 @@ ExamRegistered: Registered for the exam ExamNotRegistered: Not registered for the exam ExamRegistration: Exam registration ExamLoginToRegister: Your need to login to Uni2work before you can register for this course. +ExamRegistrationTime: Registered since ExamRegisterToMustBeAfterRegisterFrom: "Register to" must be after "register from" ExamDeregisterUntilMustBeAfterRegisterFrom: "Deregister until" must be after "register from" @@ -1712,6 +1780,20 @@ ExamUsersResultsReset count: Successfully reset result for #{show count} #{plura ExamUsersPartResultsSet count: Successfully modified exam part result for #{show count} #{pluralEN count "participant" "participants"} ExamUsersBonusSet count: Successfully modified exam bonus for #{show count} #{pluralEN count "participant" "participants"} ExamUsersResultSet count: Sucessfully modified exam result for #{show count} #{pluralEN count "participant" "participants"} +CourseUserTutorialsDeregistered count: Sucessfully deregistered participant from #{show count} #{pluralEN count "tutorial" "tutorials"} +CourseUserNoTutorialsDeregistered: Participant is not registered for any of the selected tutorials +CourseUserExamsDeregistered count: Successfully deregistered participant from #{show count} #{pluralEN count "exam" "exams"} +CourseUserNoExamsDeregistered: Participant is not registered for any of the selected exams +CourseUserExamsResultSet count: Successfully adjusted the participant's result for #{show count} #{pluralEN count "exam" "exams"} +CourseUserExamResultDoesNotMatchMode examn: The chosen result does not match the grading mode for exam “#{examn}” +CourseUserSetSubmissionGroup: Set/Unset registered submission group +CourseUsersSubmissionGroupSetNew count: Successfully added #{show count} #{pluralEN count "user" "users"} to submission group +CourseUsersSubmissionGroupUnset count: Successfully removed #{show count} #{pluralEN count "user" "users"} from their #{pluralEN count "submission group" "respective submission groups"} +CourseUsersStateSet count: Successfully changed state of #{show count} #{pluralEN count "user" "users"} + +SubmissionGroup: Registered submission group +NoSubmissionGroup: No registered submission group +SubmissionGroupEmptyIsUnsetTip: Leave empty to remove users from their respective submission groups ExamUserSynchronised: Synchronised ExamUserSyncOfficeName: Name @@ -1750,6 +1832,7 @@ CsvDeleteMissing: Delete missing entries BtnCsvExport: Export CSV file BtnCsvImport: Import CSV file BtnCsvImportConfirm: Finalise CSV import +BtnCsvImportAbort: Abort CsvImportNotConfigured: CSV import not configured CsvImportConfirmationHeading: CSV import preview (no changes have been made yet) @@ -1758,6 +1841,8 @@ CsvImportUnnecessary: Importing the given CSV file does not correspond to perfor CsvImportSuccessful n: Successfully imported CSV file. #{n} #{pluralEN n "edit" "edits"} have been performed. CsvImportAborted: CSV import aborted CsvImportExplanationLabel: Informating regarding CSV import +CsvExampleData: Example data +CsvExportExample: Export example CSV Proportion c of prop: #{c}/#{of} (#{rationalToFixed2 (100 * prop)}%) ProportionNoRatio c of: #{c}/#{of} @@ -1798,6 +1883,8 @@ CsvColumnUserSemester: Semester the participant is in wrt. to their associated f CsvColumnUserRegistration: Time of participant's enrollment (ISO 8601) CsvColumnUserNote: Course notes for the participant CsvColumnUserTutorial: Tutorials which the user is registered for, separated by semicolon (;). For each registration group among the tutorials there is a separate column. The registration group columns contain at most one tutorial per participant. If every tutorial has a registration group there is no column "tutorial". +CsvColumnUserExam: Exams which the user is registered for, separated by semicolon (;). +CsvColumnUserSubmissionGroup: Registered submission group CsvColumnExamOfficeExamUserOccurrenceStart: Exam occurrence (ISO 8601) @@ -1821,6 +1908,8 @@ DBCsvDuplicateKey: Two rows in the CSV file reference the same database entry an DBCsvDuplicateKeyTip: Please remove one of the lines listed below and try again. DBCsvKeyException: For a row in the CSV file it could not be determined whether it references any database entry. DBCsvException: An error occurred hile computing the set of edits this CSV import corresponds to. +DBCsvParseError: An uploaded file could not be interpreted as CSV of the expected format. +DBCsvParseErrorTip: The Uni2work-component that handles CSV decoding has reported the following error: ExamUserCsvCourseRegister: Register users for the exam and enroll them in the course ExamUserCsvRegister: Register users for the exam @@ -1837,10 +1926,11 @@ ExamBonusNone: No bonus points ExamUserCsvCourseNoteDeleted: Course note will be deleted -ExamUserCsvExceptionNoMatchingUser: Course participant could not be identified uniquely -ExamUserCsvExceptionNoMatchingStudyFeatures: The specified field did not match with any of the participant's fields of study -ExamUserCsvExceptionNoMatchingOccurrence: Occurrence/room could not be identified uniquely +ExamUserCsvExceptionNoMatchingUser: Course participant could not be identified uniquely. All identifiers (given name(s), surname, display name, matriculation, ..) must match exactly. You can try to remove some of the identifiers for the given line (i.e. all but matriculation). Uni2work will then search for users using only the remaining identifiers. In this case special care should be taken that Uni2work correctly identifies the intended user. +ExamUserCsvExceptionNoMatchingStudyFeatures: The specified field did not match with any of the participant's fields of study. You can try to remove the field of study for the given line. Uni2work will then automatically choose a field of study. +ExamUserCsvExceptionNoMatchingOccurrence: Occurrence/room could not be identified uniquely. Please ensure that the given line only contains internal room identifiers exactly as they have been configured for this exam. ExamUserCsvExceptionMismatchedGradingMode expectedGradingMode actualGradingMode: The imported data contained an exam achievement which does not match the grading mode for this exam. The expected grading mode can be changed at "Edit exam" ("Passed/Failed", "Numeric grades", or "Mixed"). +ExamUserCsvExceptionNoOccurrenceTime: The imported data contained an exam achievement without an associated time. You can either enter a time for each student in the appropriate column or you can set a default time for the entire exam under "Edit". ExternalExamUserCsvRegister: Store exam achievement ExternalExamUserCsvSetTime: Adjust exam time @@ -2011,6 +2101,7 @@ SchoolAdmin: Admin SchoolLecturer: Lecturer SchoolEvaluation: Course evaluation SchoolExamOffice: Exam office +SchoolAllocation: Administration of central allocations ApplicationEditTip: During the application period you may edit and retract your applications at will. @@ -2171,8 +2262,13 @@ CourseNewsDeleteQuestion: Are you sure you want to delete the item of course new CourseNewsDeleted: Successfully deleted item of course news CourseDeregistrationAllocationLog: Your enrollment in this course is due to a central allocation. If you leave the course, this will be permanently recorded and might affect you negatively in future central allocations. If you have good reasons why you should not be held accountable for leaving the course, please contact a course administrator. Course administrators can deregister you without incurring a permanent record. +CourseDeregistrationNoShow: If you deregister from this course “no show” will be recorded as your exam achievement for all exams associated with this course. If you have good reasons why you shold not be held accountable for leaving the course, please contact a course administrator. Course administrators can deregister you without incurring a permanent record. CourseDeregistrationAllocationReason: Reason CourseDeregistrationAllocationReasonTip: The specified reason will be permanently stored and might be the only information available during conflict resolution +CourseDeregistrationAllocationNoShow: Record as “no show” +CourseDeregistrationAllocationNoShowTip: Should, for all exams associated with this course, “no show” be recorded as the exam achievement automatically? This would be done once immediately (if no other achievement exists for the given exam) and automatically whenever a new exam is created. +CourseDeregisterNoShow: Record “no show” when deregistering +CourseDeregisterNoShowTip: Should “no show” be recorded as the exam achievement for all exams associated with this course automatically whenever a course participant deregisters themselves? This would be done once upon deregistration (if no other achievement exists for the given exam) and automatically whenever a new exam is created. CourseDeregistrationAllocationShouldLog: Self imposed CourseDeregistrationAllocationShouldLogTip: If the participant was enrolled in this course due to a central allocation, it is intended that a permanent record be made that might affect the student negatively in future central allocations. As a course administrator you have the right to prevent this if the participant can present good reasons why them leaving the course is not self imposed. @@ -2194,11 +2290,14 @@ FavouriteParticipant: Your courses FavouriteManual: Favourites FavouriteCurrent: Current course +FavouritesUnavailableTip: Quick Actions for this course are currently not available. + CourseEvents: Occurrences CourseEventType: Type CourseEventTypePlaceholder: Lecture, Exercise discussion, ... CourseEventTime: Time CourseEventRoom: Regular room +CourseEventNote: Note CourseEventActions: Actions CourseEventsActionEdit: Edit CourseEventsActionDelete: Delete @@ -2344,3 +2443,97 @@ InfoLecturerAllocations: Central allocations ParticipantsIntersectCourseOption tid@TermId ssh@SchoolId coursen@CourseName: #{tid} - #{ssh} - #{coursen} ParticipantsIntersectCourses: Courses + +AllocationUsersTitle tid ssh ash: #{tid}-#{ssh}-#{ash}: Applicants +AllocationUsersApplied: Applications +AllocationUsersAssigned: Assignments +AllocationUsersVetoed: Vetos +AllocationUsersRequested: Requested assignments +AllocationUsersPriority: Central priority + +CsvColumnAllocationUserSurname: Applicant's surname(s) +CsvColumnAllocationUserFirstName: Applicants's first name(s) +CsvColumnAllocationUserName: Applicant's full name +CsvColumnAllocationUserMatriculation: Applicant's matriculation +CsvColumnAllocationUserRequested: Maximum number of placements the applicant is prepared to accept +CsvColumnAllocationUserApplied: Number of applications the applicant has provided +CsvColumnAllocationUserVetos: Number of applications that have received a veto from a course administrator or have been rated with a grade that is equivalent to "failed" (5.0) +CsvColumnAllocationUserAssigned: Number of assignments the applicant has already received +CsvColumnAllocationUserPriority: Central priority of this applicant; either a number based on the applicants position in the list sorted by priority (higher numbers mean a higher priority) or a comma-separated list of numerical priorities in square brackets (e.g. [1, 2, 3]) +AllocationUsersCsvName tid ssh ash: #{foldCase (termToText (unTermKey tid))}-#{foldedCase (unSchoolKey ssh)}-#{foldedCase ash}-applicants + +AllocationPrioritiesMode: Mode +AllocationPrioritiesNumeric: Numeric priorities +AllocationPrioritiesOrdinal: Priorities based on sorted list +AllocationPrioritiesTitle tid ssh ash: #{tid}-#{ssh}-#{ash}: Central priorities +AllocationPrioritiesFile: CSV file +AllocationPrioritiesSunk num: Successfully registered central priorities for #{num} #{pluralEN num "applicant" "applicants"} +AllocationPrioritiesMissing num: Could not register central priorities for #{num} #{pluralEN num "applicant" "applicants"} because their matriculation was not found in the uploaded CSV file +AllocationMissingPrioritiesIgnored: Applicants for whom no central priority has been registered will be ignored during assignment! + +ExampleUser1FirstName: Max SecondName +ExampleUser1Surname: Mustermann +ExampleUser1DisplayName: Max Mustermann +ExampleUser2FirstName: Martha +ExampleUser2Surname: Musterstudent +ExampleUser2DisplayName: Musterstudent Martha +ExampleUser3FirstName: Maria +ExampleUser3Surname: Example +ExampleUser3DisplayName: Example + +AllocationUsersMissingPriorities: Participants without central priority +AllocationUsersMissingPrioritiesTip: Care must be taken, that no participant is excluded from the allocation by not having been assigned a central priority. +AllocationUsersMissingPrioritiesOk: It was ensured, that all participants mentioned above, are excluded from the allocation on valid grounds. +AllocationRestrictCourses: Restrict courses +AllocationRestrictCoursesTip: Should places be assigned only in a subset of courses? This functionality can be used to make alternate placements in the case that some participants withdraw from their assigned courses. This functionality should only be used to exclude courses on valid grounds. E.g. if a seminar already had a planning meeting and is thus unable to accept new participants. +AllocationRestrictCoursesSelection: Courses +AllocationRestrictCoursesSelectionTip: Participants will only be assigned to courses listed here. +AllocationUsersMissingPrioritiesNotOk: Central allocation cannot occur until all participants, that were not excluded explicitly (“Participants without central priority”), have been assigned a central priority! +AllocationComputed: A possible allocation has been computed and stored in your session. No changes have yet been made! +AllocationOnlyCompute: By sending this form a possible allocation will be computed and saved temporarily. You can then check that the computed allocation is as expected. No changes will yet be made to the state of the database and no notifications will be sent. +AllocationAcceptFormDoesNotMatchSession: The form to accept the computed allocation was generated for a different result than the one, that is currently saved in your session. +ComputedAllocation: Computed allocation +AllocationAccepted: Successfully saved allocation +AllocationMatchedUsers: Newly assigned +AllocationUnmatchedUsers: Participants without assigned places +AllocationUnmatchedCourses: Courses without assigned participants +AllocationTime: Time of allocation +AllocationRequestedPlaces: Requested places +AllocationOfferedPlaces: Offered places +AllocationUserNewMatches: New allocations +AllocationUsersCount: Participants +AllocationCoursesCount: Courses + +CourseOption tid ssh coursen: #{tid} - #{ssh} - #{coursen} + +BearerTokenUsageWarning: Using this interface you are able to encode essentially arbitrary permissions inte bearer tokens. This allows you to freely hand permissions off arbitrarily and without relevant restrictions. Only use this interface if you have discussed the consequences of the specific token, that you want to issue, with an experienced developer! +BearerTokenAuthorityGroups: Authority (groups) +BearerTokenAuthorityGroupsTip: All primary users of the groups listed here need to have the requisite permissions to access a route in order for the created token to grant permission to do so as well. +BearerTokenAuthorityUsers: Authority (users +BearerTokenAuthorityUsersTip: All users listed here need to have the requisite permissions to access a route in order for the created token to grant permission to do so as well. The user issuing the token using this interface also needs to have permission to access that route (they are automatically added to the list of authorities). +BearerTokenAuthorityUnknownUser email: Could not find any user with email #{email} +BearerTokenRoutes: Permitted routes +BearerTokenRoutesTip: If the token is restricted to certain routes and no routes are listed, the token is valid nowhere. +BearerTokenRestrictions: Route-specific restrictions +BearerTokenRestrictRoutes: Restrict token to certain routes +BearerTokenAdditionalAuth: Additional authorisation +BearerTokenAdditionalAuthTip: If nothing is entered, no additional authorisation will be performed when the token is used. Mere posession of the token will be sufficient. +BearerTokenOverrideExpiration: Override expiration time +BearerTokenExpires: Expiration time +BearerTokenExpiresTip: If no expiration time is given, the token will not expire. It will be valid forever. +BearerTokenOverrideStart: Start time +BearerTokenOverrideStartTip: If no start time is given, only the expiration time will be checked when the token is used. + +FaqTitle: Frequently asked questions +AdditionalFaqs: More frequently asked questions + +MultiActionUnknownAction: In a form dependent on the value of a field a value was given for which no form is available + +CourseParticipantStateIsActive: Active participants +CourseParticipantStateIsInactive: Former participants +CourseParticipantStateIsActiveFilter: View +CourseUserReRegister: Re-register +CourseParticipantActive: Participant +CourseParticipantInactive: Deregistered +CourseParticipantNoShow: No show +CourseUserState: State diff --git a/missing-translations.sh b/missing-translations.sh index 1dc0db869..b335ec902 100755 --- a/missing-translations.sh +++ b/missing-translations.sh @@ -6,6 +6,17 @@ typeset -a requiredLangs requiredLangs=(de en) +fix=1 +while getopts ':f' arg; do + case $arg in + f) fix=0 ;; + \*) print nothing: $OPTARG; exit 2;; + \?) print invalid option: $OPTARG; exit 2;; + esac +done +shift $OPTIND-1 + + function translations() { msgFile=$1 @@ -67,8 +78,32 @@ for msgDirectory (${msgDirectories}); do done # printf ">>> %s\n" ${msgDirectory} - diff -u0 --suppress-common-lines -wB ${diffArgs} | grep -vE '^@@.*@@' + if [[ $fix != 0 ]]; then + if [[ ${#dirMsgFiles} -gt 1 ]]; then + diff -u0 --suppress-common-lines -wB ${diffArgs} | grep -vE '^@@.*@@' + diffStatus=$pipestatus[0] + else + diffStatus=1 + fi + else + if [[ ${#dirMsgFiles} -gt 1 ]]; then + diff -u0 --suppress-common-lines -wB ${diffArgs} >/dev/null + diffStatus=$? + else + diffStatus=1 + fi + + if [[ ${diffStatus} == 1 ]]; then + ./translate.hs msgs ${dirMsgFiles} && diffStatus=0 + fi + fi + + return ${diffStatus} ) || msgDifference=1 + + if [[ $fix == 0 && $msgDifference != 0 ]]; then + exit 1 + fi done @@ -89,19 +124,46 @@ for templateDirectory (templates/i18n/**/*(FN)); do fi done + typeset -a templatePrefixes + templatePrefixes=() + for templateFile (${templateFiles}); do + [[ ${templateFile:h} == ${templateDirectory} ]] || continue + + templatePrefix=$(sed -r 's/^(.*\.)?[^.]+\.[^.]+$/\1/' <<<"${templateFile:t}") + + if ! ((${templatePrefixes[(Ie)${templatePrefix}]})); then + templatePrefixes+=("${templatePrefix}") + fi + done + + # printf "%d %s\n" ${#templatePrefixes} "${templatePrefixes}" + for ext (${templateExtensions}); do for lang (${requiredLangs}); do - foundLang=0 - for templateFile (${templateDirectory}/*.${ext}); do - [[ ${templateFile:t} =~ "(^|.)${lang}[-.]" ]] || continue - foundLang=1 - break - done - - if [[ $foundLang -ne 1 ]]; then - templateDifference=1 - printf "%s: %s (%s)\n" $templateDirectory $lang $ext - fi + for prefixQ (${(q)templatePrefixes}); do + prefix=${(Q)prefixQ} + # printf ">> %s %s %s\n" ${prefix} ${lang} ${ext} + + foundLang=1 + for templateFile (${templateDirectory}/*.${ext}); do + # printf "%s\n" ${templateFile} + [[ ${templateFile:t} =~ "^${prefix}${lang}[-.]" ]] || continue + # printf "match\n" + foundLang=0 + break + done + + # printf ">> %s\n" ${foundLang} + + if [[ $foundLang -ne 0 ]]; then + templateDifference=1 + [[ $fix != 0 ]] && printf "%s: %s*.%s (%s)\n" "$templateDirectory" "$prefix" "$ext" "$lang" + + if [[ $fix == 0 ]]; then + ./translate.hs dir $templateDirectory && templateDifference=0 + fi + fi + done done done done diff --git a/models/allocations.model b/models/allocations.model index 7cbfe58bc..a1d254dda 100644 --- a/models/allocations.model +++ b/models/allocations.model @@ -21,6 +21,7 @@ Allocation -- attributes with prefix staff- affect lecturers only, but are invis registerByCourse UTCTime Maybe -- course registration dates are ignored until this day has passed or always prohibited overrideDeregister UTCTime Maybe -- course deregistration enforced to be this date, i.e. students may disenrol from course after or never -- overrideVisible not needed, since courses are always visible + matchingSeed ByteString default='\x'::bytea TermSchoolAllocationShort term school shorthand -- shorthand must be unique within school and semester TermSchoolAllocationName term school name -- name must be unique within school and semester deriving Show Eq Ord Generic @@ -28,6 +29,7 @@ Allocation -- attributes with prefix staff- affect lecturers only, but are invis AllocationMatching allocation AllocationId fingerprint AllocationFingerprint + time UTCTime log FileId AllocationCourse diff --git a/models/courses.model b/models/courses.model index bea256759..21bd6132b 100644 --- a/models/courses.model +++ b/models/courses.model @@ -15,6 +15,7 @@ Course -- Information about a single course; contained info is always visible registerFrom UTCTime Maybe -- enrolement allowed from a given day onwwards or prohibited registerTo UTCTime Maybe -- enrolement may be prohibited from a given date onwards deregisterUntil UTCTime Maybe -- unenrolement may be prohibited from a given date onwards + deregisterNoShow Bool default=false registerSecret Text Maybe -- enrolement maybe protected by a simple common passphrase materialFree Bool -- False: only enrolled users may see course materials not stored in this table applicationsRequired Bool default=false @@ -30,6 +31,7 @@ CourseEvent course CourseId room Text time Occurrences + note Html Maybe lastChanged UTCTime default=now() CourseAppInstructionFile @@ -52,6 +54,7 @@ CourseParticipant -- course enrolement registration UTCTime -- time of last enrolement for this course field StudyFeaturesId Maybe -- associated degree course, user-defined; required for communicating grades allocated AllocationId Maybe -- participant was centrally allocated + state CourseParticipantState UniqueParticipant user course -- Replace the last two by the following, once an audit log is available -- CourseUserNote -- lecturers of a specific course may share a text note on each enrolled student diff --git a/models/files.model b/models/files.model index 2ea0569ef..6b2324e55 100644 --- a/models/files.model +++ b/models/files.model @@ -8,7 +8,5 @@ File deriving Show Eq Generic SessionFile - user UserId - reference SessionFileReference file FileId touched UTCTime \ No newline at end of file diff --git a/models/sheets.model b/models/sheets.model index fcd2cadc4..418590e52 100644 --- a/models/sheets.model +++ b/models/sheets.model @@ -12,6 +12,7 @@ Sheet -- exercise sheet for a given course solutionFrom UTCTime Maybe -- Solution is made available submissionMode SubmissionMode -- Submission upload by students and/or through tutors? autoDistribute Bool default=false -- Should correctors be assigned submissions automagically? + anonymousCorrection Bool default=true CourseSheet course name deriving Generic SheetEdit -- who edited when a row in table "Course", kept indefinitely diff --git a/models/submissions.model b/models/submissions.model index e8ea0d049..c29e0373f 100644 --- a/models/submissions.model +++ b/models/submissions.model @@ -23,12 +23,9 @@ SubmissionUser -- which submission belongs to whom UniqueSubmissionUser user submission -- multiple users may share same submission, in case of (ad-hoc) submission groups SubmissionGroup -- pre-defined submission groups; some courses only allow pre-defined submission groups course CourseId - name Text Maybe -SubmissionGroupEdit -- who edited a submissionGroup when? - user UserId - time UTCTime - submissionGroup SubmissionGroupId + name SubmissionGroupName + UniqueSubmissionGroup course name SubmissionGroupUser -- Registered submission groups, just for checking upon submission, but independent of actual SubmissionUser submissionGroup SubmissionGroupId user UserId - UniqueSubmissionGroupUser submissionGroup user + UniqueSubmissionGroupUser submissionGroup user diff --git a/models/system-messages.model b/models/system-messages.model index f2692ab64..1ba853a41 100644 --- a/models/system-messages.model +++ b/models/system-messages.model @@ -3,8 +3,12 @@ SystemMessage from UTCTime Maybe -- Message is not shown before this date has passed (never shown, if null) to UTCTime Maybe -- Message is shown until this date has passed (shown forever, if null) + newsOnly Bool default=false authenticatedOnly Bool -- Show message to all users upon visiting the site or only upon login? severity MessageStatus -- Success, Warning, Error, Info, ... + created UTCTime default=now() + lastChanged UTCTime default=now() + lastUnhide UTCTime default=now() defaultLanguage Lang -- Language of @content@ and @summary@ content Html -- Detailed message shown when clicking on the @summary@-popup or when no @summary@ is specified summary Html Maybe @@ -14,3 +18,9 @@ SystemMessageTranslation -- Translation of a @SystemMessage@ into another langua content Html summary Html Maybe UniqueSystemMessageTranslation message language + +SystemMessageHidden + message SystemMessageId + user UserId + time UTCTime + UniqueSystemMessageHidden user message \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 3faf37b79..f1b048ed8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,13 @@ { "name": "uni2work", - "version": "13.0.1", + "version": "16.0.5", "lockfileVersion": 1, "requires": true, "dependencies": { "@babel/cli": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.7.5.tgz", - "integrity": "sha512-y2YrMGXM3NUyu1Myg0pxg+Lx6g8XhEyvLHYNRwTBV6fDek3H7Io6b7N/LXscLs4HWn4HxMdy7f2rM1rTMp2mFg==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.8.4.tgz", + "integrity": "sha512-XXLgAm6LBbaNxaGhMAznXXaxtCWfuv6PIDJ9Alsy9JYTOh+j2jJz+L/162kkfU1j/pTSxK1xGmlwI4pdIMkoag==", "dev": true, "requires": { "chokidar": "^2.1.8", @@ -22,9 +22,9 @@ }, "dependencies": { "commander": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.0.1.tgz", - "integrity": "sha512-IPF4ouhCP+qdlcmCedhxX4xiGBPyigb8v5NeUp+0LyhwLgxMqyp3S0vl7TAPfS/hiP7FC3caI/PB9lTmP8r1NA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", "dev": true } } @@ -38,22 +38,73 @@ "@babel/highlight": "^7.0.0" } }, - "@babel/core": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.5.tgz", - "integrity": "sha512-M42+ScN4+1S9iB6f+TL7QBpoQETxbclx+KNoKJABghnKYE+fMzSGqst0BZJc8CpI625bwPwYgUyRvxZ+0mZzpw==", + "@babel/compat-data": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.9.6.tgz", + "integrity": "sha512-5QPTrNen2bm7RBc7dsOmcA5hbrS4O2Vhmk5XOL4zWW/zD/hV0iinpefDlkm+tBBy8kDtFaaeEvmAqt+nURAV2g==", "dev": true, "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.4", - "@babel/helpers": "^7.7.4", - "@babel/parser": "^7.7.5", - "@babel/template": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4", + "browserslist": "^4.11.1", + "invariant": "^2.2.4", + "semver": "^5.5.0" + }, + "dependencies": { + "browserslist": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.12.0.tgz", + "integrity": "sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001043", + "electron-to-chromium": "^1.3.413", + "node-releases": "^1.1.53", + "pkg-up": "^2.0.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001051", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001051.tgz", + "integrity": "sha512-sw8UUnTlRevawTMZKN7vpfwSjCBVoiMPlYd8oT2VwNylyPCBdMAUmLGUApnYYTtIm5JXsQegUAY7GPHqgfDzjw==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.428", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.428.tgz", + "integrity": "sha512-u3+5jEfgLKq/hGO96YfAoOAM1tgFnRDTCD5mLuev44tttcXix+INtVegAkmGzUcfDsnzkPt51XXurXZLLwXt0w==", + "dev": true + }, + "node-releases": { + "version": "1.1.54", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.54.tgz", + "integrity": "sha512-tLzytKpgwKQr37yw9CEODjNM9lnmsNxzlv575GzOZ16AgMvPcJis/DgrJX4UEV1KIYoXk6XoVfY6YaMOPJESAQ==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "@babel/core": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.6.tgz", + "integrity": "sha512-nD3deLvbsApbHAHttzIssYqgb883yU/d9roe4RZymBCDaZryMJDbptVpEpeQuRh4BJ+SYI8le9YGxKvFEvl1Wg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.9.6", + "@babel/helper-module-transforms": "^7.9.0", + "@babel/helpers": "^7.9.6", + "@babel/parser": "^7.9.6", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.9.6", + "@babel/types": "^7.9.6", "convert-source-map": "^1.7.0", "debug": "^4.1.0", - "json5": "^2.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", "lodash": "^4.17.13", "resolve": "^1.3.2", "semver": "^5.4.1", @@ -61,98 +112,23 @@ }, "dependencies": { "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", "dev": true, "requires": { - "@babel/highlight": "^7.0.0" + "@babel/highlight": "^7.8.3" } }, - "@babel/generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.4.tgz", - "integrity": "sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==", + "@babel/highlight": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", + "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", "dev": true, "requires": { - "@babel/types": "^7.7.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.5.tgz", - "integrity": "sha512-KNlOe9+/nk4i29g0VXgl8PEXIRms5xKLJeuZ6UptN0fHv+jDiriG+y94X6qAgWTR0h3KaoM1wK5G5h7MHFRSig==", - "dev": true - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/traverse": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", - "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" + "@babel/helper-validator-identifier": "^7.9.0", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" } }, "convert-source-map": { @@ -188,1177 +164,290 @@ } }, "@babel/generator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", - "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.6.tgz", + "integrity": "sha512-+htwWKJbH2bL72HRluF8zumBxzuX0ZZUFl3JLNyoUjM/Ho8wnVpPXM6aUz8cfKDqQ/h7zHqKt4xzJteUosckqQ==", "dev": true, "requires": { - "@babel/types": "^7.4.4", + "@babel/types": "^7.9.6", "jsesc": "^2.5.1", - "lodash": "^4.17.11", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" + "lodash": "^4.17.13", + "source-map": "^0.5.0" } }, "@babel/helper-annotate-as-pure": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.7.4.tgz", - "integrity": "sha512-2BQmQgECKzYKFPpiycoF9tlb5HA4lrVyAmLLVK177EcQAqjVLciUb2/R+n1boQ9y5ENV3uz2ZqiNw7QMBBw1Og==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz", + "integrity": "sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw==", "dev": true, "requires": { - "@babel/types": "^7.7.4" - }, - "dependencies": { - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } + "@babel/types": "^7.8.3" } }, "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.7.4.tgz", - "integrity": "sha512-Biq/d/WtvfftWZ9Uf39hbPBYDUo986m5Bb4zhkeYDGUllF43D+nUe5M6Vuo6/8JDK/0YX/uBdeoQpyaNhNugZQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz", + "integrity": "sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw==", "dev": true, "requires": { - "@babel/helper-explode-assignable-expression": "^7.7.4", - "@babel/types": "^7.7.4" - }, - "dependencies": { - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } + "@babel/helper-explode-assignable-expression": "^7.8.3", + "@babel/types": "^7.8.3" } }, - "@babel/helper-call-delegate": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.7.4.tgz", - "integrity": "sha512-8JH9/B7J7tCYJ2PpWVpw9JhPuEVHztagNVuQAFBVFYluRMlpG7F1CgKEgGeL6KFqcsIa92ZYVj6DSc0XwmN1ZA==", + "@babel/helper-compilation-targets": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.9.6.tgz", + "integrity": "sha512-x2Nvu0igO0ejXzx09B/1fGBxY9NXQlBW2kZsSxCJft+KHN8t9XWzIvFxtPHnBOAXpVsdxZKZFbRUC8TsNKajMw==", "dev": true, "requires": { - "@babel/helper-hoist-variables": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4" + "@babel/compat-data": "^7.9.6", + "browserslist": "^4.11.1", + "invariant": "^2.2.4", + "levenary": "^1.1.1", + "semver": "^5.5.0" }, "dependencies": { - "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "browserslist": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.12.0.tgz", + "integrity": "sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg==", "dev": true, "requires": { - "@babel/highlight": "^7.0.0" + "caniuse-lite": "^1.0.30001043", + "electron-to-chromium": "^1.3.413", + "node-releases": "^1.1.53", + "pkg-up": "^2.0.0" } }, - "@babel/generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.4.tgz", - "integrity": "sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.5.tgz", - "integrity": "sha512-KNlOe9+/nk4i29g0VXgl8PEXIRms5xKLJeuZ6UptN0fHv+jDiriG+y94X6qAgWTR0h3KaoM1wK5G5h7MHFRSig==", + "caniuse-lite": { + "version": "1.0.30001051", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001051.tgz", + "integrity": "sha512-sw8UUnTlRevawTMZKN7vpfwSjCBVoiMPlYd8oT2VwNylyPCBdMAUmLGUApnYYTtIm5JXsQegUAY7GPHqgfDzjw==", "dev": true }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } + "electron-to-chromium": { + "version": "1.3.428", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.428.tgz", + "integrity": "sha512-u3+5jEfgLKq/hGO96YfAoOAM1tgFnRDTCD5mLuev44tttcXix+INtVegAkmGzUcfDsnzkPt51XXurXZLLwXt0w==", + "dev": true }, - "@babel/traverse": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", - "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - } + "node-releases": { + "version": "1.1.54", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.54.tgz", + "integrity": "sha512-tLzytKpgwKQr37yw9CEODjNM9lnmsNxzlv575GzOZ16AgMvPcJis/DgrJX4UEV1KIYoXk6XoVfY6YaMOPJESAQ==", + "dev": true }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true } } }, "@babel/helper-create-class-features-plugin": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.7.4.tgz", - "integrity": "sha512-l+OnKACG4uiDHQ/aJT8dwpR+LhCJALxL0mJ6nzjB25e5IPwqV1VOsY7ah6UB1DG+VOXAIMtuC54rFJGiHkxjgA==", + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.9.6.tgz", + "integrity": "sha512-6N9IeuyHvMBRyjNYOMJHrhwtu4WJMrYf8hVbEHD3pbbbmNOk1kmXSQs7bA4dYDUaIx4ZEzdnvo6NwC3WHd/Qow==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-member-expression-to-functions": "^7.7.4", - "@babel/helper-optimise-call-expression": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4" - }, - "dependencies": { - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.5.tgz", - "integrity": "sha512-KNlOe9+/nk4i29g0VXgl8PEXIRms5xKLJeuZ6UptN0fHv+jDiriG+y94X6qAgWTR0h3KaoM1wK5G5h7MHFRSig==", - "dev": true - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } + "@babel/helper-function-name": "^7.9.5", + "@babel/helper-member-expression-to-functions": "^7.8.3", + "@babel/helper-optimise-call-expression": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-replace-supers": "^7.9.6", + "@babel/helper-split-export-declaration": "^7.8.3" } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.7.4.tgz", - "integrity": "sha512-Mt+jBKaxL0zfOIWrfQpnfYCN7/rS6GKx6CCCfuoqVVd+17R8zNDlzVYmIi9qyb2wOk002NsmSTDymkIygDUH7A==", + "version": "7.8.8", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.8.tgz", + "integrity": "sha512-LYVPdwkrQEiX9+1R29Ld/wTrmQu1SSKYnuOk3g0CkcZMA1p0gsNxJFj/3gBdaJ7Cg0Fnek5z0DsMULePP7Lrqg==", "dev": true, "requires": { - "@babel/helper-regex": "^7.4.4", - "regexpu-core": "^4.6.0" + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-regex": "^7.8.3", + "regexpu-core": "^4.7.0" } }, "@babel/helper-define-map": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.7.4.tgz", - "integrity": "sha512-v5LorqOa0nVQUvAUTUF3KPastvUt/HzByXNamKQ6RdJRTV7j8rLL+WB5C/MzzWAwOomxDhYFb1wLLxHqox86lg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz", + "integrity": "sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.7.4", - "@babel/types": "^7.7.4", + "@babel/helper-function-name": "^7.8.3", + "@babel/types": "^7.8.3", "lodash": "^4.17.13" - }, - "dependencies": { - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.5.tgz", - "integrity": "sha512-KNlOe9+/nk4i29g0VXgl8PEXIRms5xKLJeuZ6UptN0fHv+jDiriG+y94X6qAgWTR0h3KaoM1wK5G5h7MHFRSig==", - "dev": true - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } } }, "@babel/helper-explode-assignable-expression": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.7.4.tgz", - "integrity": "sha512-2/SicuFrNSXsZNBxe5UGdLr+HZg+raWBLE9vC98bdYOKX/U6PY0mdGlYUJdtTDPSU0Lw0PNbKKDpwYHJLn2jLg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz", + "integrity": "sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw==", "dev": true, "requires": { - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.4.tgz", - "integrity": "sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.5.tgz", - "integrity": "sha512-KNlOe9+/nk4i29g0VXgl8PEXIRms5xKLJeuZ6UptN0fHv+jDiriG+y94X6qAgWTR0h3KaoM1wK5G5h7MHFRSig==", - "dev": true - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/traverse": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", - "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" } }, "@babel/helper-function-name": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", - "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz", + "integrity": "sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.0.0", - "@babel/template": "^7.1.0", - "@babel/types": "^7.0.0" + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.9.5" } }, "@babel/helper-get-function-arity": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", - "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", "dev": true, "requires": { - "@babel/types": "^7.0.0" + "@babel/types": "^7.8.3" } }, "@babel/helper-hoist-variables": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.7.4.tgz", - "integrity": "sha512-wQC4xyvc1Jo/FnLirL6CEgPgPCa8M74tOdjWpRhQYapz5JC7u3NYU1zCVoVAGCE3EaIP9T1A3iW0WLJ+reZlpQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz", + "integrity": "sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg==", "dev": true, "requires": { - "@babel/types": "^7.7.4" - }, - "dependencies": { - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } + "@babel/types": "^7.8.3" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.7.4.tgz", - "integrity": "sha512-9KcA1X2E3OjXl/ykfMMInBK+uVdfIVakVe7W7Lg3wfXUNyS3Q1HWLFRwZIjhqiCGbslummPDnmb7vIekS0C1vw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz", + "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==", "dev": true, "requires": { - "@babel/types": "^7.7.4" - }, - "dependencies": { - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } + "@babel/types": "^7.8.3" } }, "@babel/helper-module-imports": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.7.4.tgz", - "integrity": "sha512-dGcrX6K9l8258WFjyDLJwuVKxR4XZfU0/vTUgOQYWEnRD8mgr+p4d6fCUMq/ys0h4CCt/S5JhbvtyErjWouAUQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz", + "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==", "dev": true, "requires": { - "@babel/types": "^7.7.4" - }, - "dependencies": { - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } + "@babel/types": "^7.8.3" } }, "@babel/helper-module-transforms": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.7.5.tgz", - "integrity": "sha512-A7pSxyJf1gN5qXVcidwLWydjftUN878VkalhXX5iQDuGyiGK3sOrrKKHF4/A4fwHtnsotv/NipwAeLzY4KQPvw==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz", + "integrity": "sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.7.4", - "@babel/helper-simple-access": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4", + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.6", + "@babel/helper-simple-access": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/template": "^7.8.6", + "@babel/types": "^7.9.0", "lodash": "^4.17.13" - }, - "dependencies": { - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.5.tgz", - "integrity": "sha512-KNlOe9+/nk4i29g0VXgl8PEXIRms5xKLJeuZ6UptN0fHv+jDiriG+y94X6qAgWTR0h3KaoM1wK5G5h7MHFRSig==", - "dev": true - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } } }, "@babel/helper-optimise-call-expression": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.7.4.tgz", - "integrity": "sha512-VB7gWZ2fDkSuqW6b1AKXkJWO5NyNI3bFL/kK79/30moK57blr6NbH8xcl2XcKCwOmJosftWunZqfO84IGq3ZZg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz", + "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==", "dev": true, "requires": { - "@babel/types": "^7.7.4" - }, - "dependencies": { - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } + "@babel/types": "^7.8.3" } }, "@babel/helper-plugin-utils": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", - "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", + "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", "dev": true }, "@babel/helper-regex": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.5.5.tgz", - "integrity": "sha512-CkCYQLkfkiugbRDO8eZn6lRuR8kzZoGXCg3149iTk5se7g6qykSpy3+hELSwquhu+TgHn8nkLiBwHvNX8Hofcw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.8.3.tgz", + "integrity": "sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ==", "dev": true, "requires": { "lodash": "^4.17.13" } }, "@babel/helper-remap-async-to-generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.7.4.tgz", - "integrity": "sha512-Sk4xmtVdM9sA/jCI80f+KS+Md+ZHIpjuqmYPk1M7F/upHou5e4ReYmExAiu6PVe65BhJPZA2CY9x9k4BqE5klw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz", + "integrity": "sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.7.4", - "@babel/helper-wrap-function": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4" - }, - "dependencies": { - "@babel/generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.4.tgz", - "integrity": "sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.5.tgz", - "integrity": "sha512-KNlOe9+/nk4i29g0VXgl8PEXIRms5xKLJeuZ6UptN0fHv+jDiriG+y94X6qAgWTR0h3KaoM1wK5G5h7MHFRSig==", - "dev": true - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/traverse": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", - "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - } - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-wrap-function": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" } }, "@babel/helper-replace-supers": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.7.4.tgz", - "integrity": "sha512-pP0tfgg9hsZWo5ZboYGuBn/bbYT/hdLPVSS4NMmiRJdwWhP0IznPwN9AE1JwyGsjSPLC364I0Qh5p+EPkGPNpg==", + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.9.6.tgz", + "integrity": "sha512-qX+chbxkbArLyCImk3bWV+jB5gTNU/rsze+JlcF6Nf8tVTigPJSI1o1oBow/9Resa1yehUO9lIipsmu9oG4RzA==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.7.4", - "@babel/helper-optimise-call-expression": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.4.tgz", - "integrity": "sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.5.tgz", - "integrity": "sha512-KNlOe9+/nk4i29g0VXgl8PEXIRms5xKLJeuZ6UptN0fHv+jDiriG+y94X6qAgWTR0h3KaoM1wK5G5h7MHFRSig==", - "dev": true - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/traverse": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", - "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } + "@babel/helper-member-expression-to-functions": "^7.8.3", + "@babel/helper-optimise-call-expression": "^7.8.3", + "@babel/traverse": "^7.9.6", + "@babel/types": "^7.9.6" } }, "@babel/helper-simple-access": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.7.4.tgz", - "integrity": "sha512-zK7THeEXfan7UlWsG2A6CI/L9jVnI5+xxKZOdej39Y0YtDYKx9raHk5F2EtK9K8DHRTihYwg20ADt9S36GR78A==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz", + "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==", "dev": true, "requires": { - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - }, - "dependencies": { - "@babel/parser": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.5.tgz", - "integrity": "sha512-KNlOe9+/nk4i29g0VXgl8PEXIRms5xKLJeuZ6UptN0fHv+jDiriG+y94X6qAgWTR0h3KaoM1wK5G5h7MHFRSig==", - "dev": true - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" } }, "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", "dev": true, "requires": { - "@babel/types": "^7.4.4" + "@babel/types": "^7.8.3" } }, + "@babel/helper-validator-identifier": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz", + "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==", + "dev": true + }, "@babel/helper-wrap-function": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.7.4.tgz", - "integrity": "sha512-VsfzZt6wmsocOaVU0OokwrIytHND55yvyT4BPB9AIIgwr8+x7617hetdJTsuGwygN5RC6mxA9EJztTjuwm2ofg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz", + "integrity": "sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4" - }, - "dependencies": { - "@babel/generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.4.tgz", - "integrity": "sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.5.tgz", - "integrity": "sha512-KNlOe9+/nk4i29g0VXgl8PEXIRms5xKLJeuZ6UptN0fHv+jDiriG+y94X6qAgWTR0h3KaoM1wK5G5h7MHFRSig==", - "dev": true - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/traverse": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", - "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - } - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } + "@babel/helper-function-name": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" } }, "@babel/helpers": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.7.4.tgz", - "integrity": "sha512-ak5NGZGJ6LV85Q1Zc9gn2n+ayXOizryhjSUBTdu5ih1tlVCJeuQENzc4ItyCVhINVXvIT/ZQ4mheGIsfBkpskg==", + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.6.tgz", + "integrity": "sha512-tI4bUbldloLcHWoRUMAj4g1bF313M/o6fBKhIsb3QnGVPwRm9JsNf/gqMkQ7zjqReABiffPV6RWj7hEglID5Iw==", "dev": true, "requires": { - "@babel/template": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4" - }, - "dependencies": { - "@babel/generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.4.tgz", - "integrity": "sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.5.tgz", - "integrity": "sha512-KNlOe9+/nk4i29g0VXgl8PEXIRms5xKLJeuZ6UptN0fHv+jDiriG+y94X6qAgWTR0h3KaoM1wK5G5h7MHFRSig==", - "dev": true - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/traverse": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", - "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - } - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.9.6", + "@babel/types": "^7.9.6" } }, "@babel/highlight": { @@ -1373,544 +462,471 @@ } }, "@babel/parser": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.5.tgz", - "integrity": "sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew==", + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.6.tgz", + "integrity": "sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q==", "dev": true }, "@babel/plugin-proposal-async-generator-functions": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.7.4.tgz", - "integrity": "sha512-1ypyZvGRXriY/QP668+s8sFr2mqinhkRDMPSQLNghCQE+GAkFtp+wkHVvg2+Hdki8gwP+NFzJBJ/N1BfzCCDEw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz", + "integrity": "sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-remap-async-to-generator": "^7.7.4", - "@babel/plugin-syntax-async-generators": "^7.7.4" + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-remap-async-to-generator": "^7.8.3", + "@babel/plugin-syntax-async-generators": "^7.8.0" } }, "@babel/plugin-proposal-class-properties": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.7.4.tgz", - "integrity": "sha512-EcuXeV4Hv1X3+Q1TsuOmyyxeTRiSqurGJ26+I/FW1WbymmRRapVORm6x1Zl3iDIHyRxEs+VXWp6qnlcfcJSbbw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.8.3.tgz", + "integrity": "sha512-EqFhbo7IosdgPgZggHaNObkmO1kNUe3slaKu54d5OWvy+p9QIKOzK1GAEpAIsZtWVtPXUHSMcT4smvDrCfY4AA==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-create-class-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-proposal-decorators": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.7.4.tgz", - "integrity": "sha512-GftcVDcLCwVdzKmwOBDjATd548+IE+mBo7ttgatqNDR7VG7GqIuZPtRWlMLHbhTXhcnFZiGER8iIYl1n/imtsg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.8.3.tgz", + "integrity": "sha512-e3RvdvS4qPJVTe288DlXjwKflpfy1hr0j5dz5WpIYYeP7vQZg2WfAEIp8k5/Lwis/m5REXEteIz6rrcDtXXG7w==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-decorators": "^7.7.4" + "@babel/helper-create-class-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-decorators": "^7.8.3" } }, "@babel/plugin-proposal-dynamic-import": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.7.4.tgz", - "integrity": "sha512-StH+nGAdO6qDB1l8sZ5UBV8AC3F2VW2I8Vfld73TMKyptMU9DY5YsJAS8U81+vEtxcH3Y/La0wG0btDrhpnhjQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz", + "integrity": "sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-dynamic-import": "^7.7.4" + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-dynamic-import": "^7.8.0" } }, "@babel/plugin-proposal-json-strings": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.7.4.tgz", - "integrity": "sha512-wQvt3akcBTfLU/wYoqm/ws7YOAQKu8EVJEvHip/mzkNtjaclQoCCIqKXFP5/eyfnfbQCDV3OLRIK3mIVyXuZlw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz", + "integrity": "sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-json-strings": "^7.7.4" + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.0" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz", + "integrity": "sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3" } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.7.4.tgz", - "integrity": "sha512-rnpnZR3/iWKmiQyJ3LKJpSwLDcX/nSXhdLk4Aq/tXOApIvyu7qoabrige0ylsAJffaUC51WiBu209Q0U+86OWQ==", + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.6.tgz", + "integrity": "sha512-Ga6/fhGqA9Hj+y6whNpPv8psyaK5xzrQwSPsGPloVkvmH+PqW1ixdnfJ9uIO06OjQNYol3PMnfmJ8vfZtkzF+A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-object-rest-spread": "^7.7.4" + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-transform-parameters": "^7.9.5" } }, "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.7.4.tgz", - "integrity": "sha512-DyM7U2bnsQerCQ+sejcTNZh8KQEUuC3ufzdnVnSiUv/qoGJp2Z3hanKL18KDhsBT5Wj6a7CMT5mdyCNJsEaA9w==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.7.4" + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz", + "integrity": "sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.0" } }, "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.7.4.tgz", - "integrity": "sha512-cHgqHgYvffluZk85dJ02vloErm3Y6xtH+2noOBOJ2kXOJH3aVCDnj5eR/lVNlTnYu4hndAPJD3rTFjW3qee0PA==", + "version": "7.8.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.8.tgz", + "integrity": "sha512-EVhjVsMpbhLw9ZfHWSx2iy13Q8Z/eg8e8ccVWt23sWQK5l1UdkoLJPN5w69UA4uITGBnEZD2JOe4QOHycYKv8A==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-create-regexp-features-plugin": "^7.8.8", + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-syntax-async-generators": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.7.4.tgz", - "integrity": "sha512-Li4+EjSpBgxcsmeEF8IFcfV/+yJGxHXDirDkEoyFjumuwbmfCVHUt0HuowD/iGM7OhIRyXJH9YXxqiH6N815+g==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.0" } }, "@babel/plugin-syntax-decorators": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.7.4.tgz", - "integrity": "sha512-0oNLWNH4k5ZbBVfAwiTU53rKFWIeTh6ZlaWOXWJc4ywxs0tjz5fc3uZ6jKAnZSxN98eXVgg7bJIuzjX+3SXY+A==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.8.3.tgz", + "integrity": "sha512-8Hg4dNNT9/LcA1zQlfwuKR8BUc/if7Q7NkTam9sGTcJphLwpf2g4S42uhspQrIrR+dpzE0dtTqBVFoHl8GtnnQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-syntax-dynamic-import": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.7.4.tgz", - "integrity": "sha512-jHQW0vbRGvwQNgyVxwDh4yuXu4bH1f5/EICJLAhl1SblLs2CDhrsmCk+v5XLdE9wxtAFRyxx+P//Iw+a5L/tTg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.0" } }, "@babel/plugin-syntax-json-strings": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.7.4.tgz", - "integrity": "sha512-QpGupahTQW1mHRXddMG5srgpHWqRLwJnJZKXTigB9RPFCCGbDGCgBeM/iC82ICXp414WeYx/tD54w7M2qRqTMg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz", + "integrity": "sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-syntax-object-rest-spread": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.7.4.tgz", - "integrity": "sha512-mObR+r+KZq0XhRVS2BrBKBpr5jqrqzlPvS9C9vuOf5ilSwzloAl7RPWLrgKdWS6IreaVrjHxTjtyqFiOisaCwg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.0" } }, "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.7.4.tgz", - "integrity": "sha512-4ZSuzWgFxqHRE31Glu+fEr/MirNZOMYmD/0BhBWyLyOOQz/gTAl7QmWm2hX1QxEIXsr2vkdlwxIzTyiYRC4xcQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" } }, "@babel/plugin-syntax-top-level-await": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.7.4.tgz", - "integrity": "sha512-wdsOw0MvkL1UIgiQ/IFr3ETcfv1xb8RMM0H9wbiDyLaJFyiDg5oZvDLCXosIXmFeIlweML5iOBXAkqddkYNizg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz", + "integrity": "sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-arrow-functions": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.7.4.tgz", - "integrity": "sha512-zUXy3e8jBNPiffmqkHRNDdZM2r8DWhCB7HhcoyZjiK1TxYEluLHAvQuYnTT+ARqRpabWqy/NHkO6e3MsYB5YfA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz", + "integrity": "sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.7.4.tgz", - "integrity": "sha512-zpUTZphp5nHokuy8yLlyafxCJ0rSlFoSHypTUWgpdwoDXWQcseaect7cJ8Ppk6nunOM6+5rPMkod4OYKPR5MUg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz", + "integrity": "sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-remap-async-to-generator": "^7.7.4" + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-remap-async-to-generator": "^7.8.3" } }, "@babel/plugin-transform-block-scoped-functions": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.7.4.tgz", - "integrity": "sha512-kqtQzwtKcpPclHYjLK//3lH8OFsCDuDJBaFhVwf8kqdnF6MN4l618UDlcA7TfRs3FayrHj+svYnSX8MC9zmUyQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz", + "integrity": "sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-block-scoping": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.7.4.tgz", - "integrity": "sha512-2VBe9u0G+fDt9B5OV5DQH4KBf5DoiNkwFKOz0TCvBWvdAN2rOykCTkrL+jTLxfCAm76l9Qo5OqL7HBOx2dWggg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz", + "integrity": "sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-plugin-utils": "^7.8.3", "lodash": "^4.17.13" } }, "@babel/plugin-transform-classes": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.7.4.tgz", - "integrity": "sha512-sK1mjWat7K+buWRuImEzjNf68qrKcrddtpQo3swi9j7dUcG6y6R6+Di039QN2bD1dykeswlagupEmpOatFHHUg==", + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.5.tgz", + "integrity": "sha512-x2kZoIuLC//O5iA7PEvecB105o7TLzZo8ofBVhP79N+DO3jaX+KYfww9TQcfBEZD0nikNyYcGB1IKtRq36rdmg==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.7.4", - "@babel/helper-define-map": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-optimise-call-expression": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-define-map": "^7.8.3", + "@babel/helper-function-name": "^7.9.5", + "@babel/helper-optimise-call-expression": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.6", + "@babel/helper-split-export-declaration": "^7.8.3", "globals": "^11.1.0" - }, - "dependencies": { - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.5.tgz", - "integrity": "sha512-KNlOe9+/nk4i29g0VXgl8PEXIRms5xKLJeuZ6UptN0fHv+jDiriG+y94X6qAgWTR0h3KaoM1wK5G5h7MHFRSig==", - "dev": true - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } } }, "@babel/plugin-transform-computed-properties": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.7.4.tgz", - "integrity": "sha512-bSNsOsZnlpLLyQew35rl4Fma3yKWqK3ImWMSC/Nc+6nGjC9s5NFWAer1YQ899/6s9HxO2zQC1WoFNfkOqRkqRQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz", + "integrity": "sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-destructuring": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.7.4.tgz", - "integrity": "sha512-4jFMXI1Cu2aXbcXXl8Lr6YubCn6Oc7k9lLsu8v61TZh+1jny2BWmdtvY9zSUlLdGUvcy9DMAWyZEOqjsbeg/wA==", + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.9.5.tgz", + "integrity": "sha512-j3OEsGel8nHL/iusv/mRd5fYZ3DrOxWC82x0ogmdN/vHfAP4MYw+AFKYanzWlktNwikKvlzUV//afBW5FTp17Q==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-dotall-regex": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.7.4.tgz", - "integrity": "sha512-mk0cH1zyMa/XHeb6LOTXTbG7uIJ8Rrjlzu91pUx/KS3JpcgaTDwMS8kM+ar8SLOvlL2Lofi4CGBAjCo3a2x+lw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz", + "integrity": "sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-create-regexp-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-duplicate-keys": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.7.4.tgz", - "integrity": "sha512-g1y4/G6xGWMD85Tlft5XedGaZBCIVN+/P0bs6eabmcPP9egFleMAo65OOjlhcz1njpwagyY3t0nsQC9oTFegJA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz", + "integrity": "sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-exponentiation-operator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.7.4.tgz", - "integrity": "sha512-MCqiLfCKm6KEA1dglf6Uqq1ElDIZwFuzz1WH5mTf8k2uQSxEJMbOIEh7IZv7uichr7PMfi5YVSrr1vz+ipp7AQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz", + "integrity": "sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ==", "dev": true, "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-for-of": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.7.4.tgz", - "integrity": "sha512-zZ1fD1B8keYtEcKF+M1TROfeHTKnijcVQm0yO/Yu1f7qoDoxEIc/+GX6Go430Bg84eM/xwPFp0+h4EbZg7epAA==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.9.0.tgz", + "integrity": "sha512-lTAnWOpMwOXpyDx06N+ywmF3jNbafZEqZ96CGYabxHrxNX8l5ny7dt4bK/rGwAh9utyP2b2Hv7PlZh1AAS54FQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.7.4.tgz", - "integrity": "sha512-E/x09TvjHNhsULs2IusN+aJNRV5zKwxu1cpirZyRPw+FyyIKEHPXTsadj48bVpc1R5Qq1B5ZkzumuFLytnbT6g==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz", + "integrity": "sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" - }, - "dependencies": { - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.5.tgz", - "integrity": "sha512-KNlOe9+/nk4i29g0VXgl8PEXIRms5xKLJeuZ6UptN0fHv+jDiriG+y94X6qAgWTR0h3KaoM1wK5G5h7MHFRSig==", - "dev": true - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-literals": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.7.4.tgz", - "integrity": "sha512-X2MSV7LfJFm4aZfxd0yLVFrEXAgPqYoDG53Br/tCKiKYfX0MjVjQeWPIhPHHsCqzwQANq+FLN786fF5rgLS+gw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz", + "integrity": "sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-member-expression-literals": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.7.4.tgz", - "integrity": "sha512-9VMwMO7i69LHTesL0RdGy93JU6a+qOPuvB4F4d0kR0zyVjJRVJRaoaGjhtki6SzQUu8yen/vxPKN6CWnCUw6bA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz", + "integrity": "sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-modules-amd": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.7.5.tgz", - "integrity": "sha512-CT57FG4A2ZUNU1v+HdvDSDrjNWBrtCmSH6YbbgN3Lrf0Di/q/lWRxZrE72p3+HCCz9UjfZOEBdphgC0nzOS6DQ==", + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.9.6.tgz", + "integrity": "sha512-zoT0kgC3EixAyIAU+9vfaUVKTv9IxBDSabgHoUCBP6FqEJ+iNiN7ip7NBKcYqbfUDfuC2mFCbM7vbu4qJgOnDw==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.7.5", - "@babel/helper-plugin-utils": "^7.0.0", - "babel-plugin-dynamic-import-node": "^2.3.0" + "@babel/helper-module-transforms": "^7.9.0", + "@babel/helper-plugin-utils": "^7.8.3", + "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.7.5.tgz", - "integrity": "sha512-9Cq4zTFExwFhQI6MT1aFxgqhIsMWQWDVwOgLzl7PTWJHsNaqFvklAU+Oz6AQLAS0dJKTwZSOCo20INwktxpi3Q==", + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.6.tgz", + "integrity": "sha512-7H25fSlLcn+iYimmsNe3uK1at79IE6SKW9q0/QeEHTMC9MdOZ+4bA+T1VFB5fgOqBWoqlifXRzYD0JPdmIrgSQ==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.7.5", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-simple-access": "^7.7.4", - "babel-plugin-dynamic-import-node": "^2.3.0" + "@babel/helper-module-transforms": "^7.9.0", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-simple-access": "^7.8.3", + "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.7.4.tgz", - "integrity": "sha512-y2c96hmcsUi6LrMqvmNDPBBiGCiQu0aYqpHatVVu6kD4mFEXKjyNxd/drc18XXAf9dv7UXjrZwBVmTTGaGP8iw==", + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.9.6.tgz", + "integrity": "sha512-NW5XQuW3N2tTHim8e1b7qGy7s0kZ2OH3m5octc49K1SdAKGxYxeIx7hiIz05kS1R2R+hOWcsr1eYwcGhrdHsrg==", "dev": true, "requires": { - "@babel/helper-hoist-variables": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "babel-plugin-dynamic-import-node": "^2.3.0" + "@babel/helper-hoist-variables": "^7.8.3", + "@babel/helper-module-transforms": "^7.9.0", + "@babel/helper-plugin-utils": "^7.8.3", + "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-umd": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.7.4.tgz", - "integrity": "sha512-u2B8TIi0qZI4j8q4C51ktfO7E3cQ0qnaXFI1/OXITordD40tt17g/sXqgNNCcMTcBFKrUPcGDx+TBJuZxLx7tw==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.9.0.tgz", + "integrity": "sha512-uTWkXkIVtg/JGRSIABdBoMsoIeoHQHPTL0Y2E7xf5Oj7sLqwVsNXOkNk0VJc7vF0IMBsPeikHxFjGe+qmwPtTQ==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-module-transforms": "^7.9.0", + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.7.4.tgz", - "integrity": "sha512-jBUkiqLKvUWpv9GLSuHUFYdmHg0ujC1JEYoZUfeOOfNydZXp1sXObgyPatpcwjWgsdBGsagWW0cdJpX/DO2jMw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz", + "integrity": "sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.7.4" + "@babel/helper-create-regexp-features-plugin": "^7.8.3" } }, "@babel/plugin-transform-new-target": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.7.4.tgz", - "integrity": "sha512-CnPRiNtOG1vRodnsyGX37bHQleHE14B9dnnlgSeEs3ek3fHN1A1SScglTCg1sfbe7sRQ2BUcpgpTpWSfMKz3gg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz", + "integrity": "sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-object-super": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.7.4.tgz", - "integrity": "sha512-ho+dAEhC2aRnff2JCA0SAK7V2R62zJd/7dmtoe7MHcso4C2mS+vZjn1Pb1pCVZvJs1mgsvv5+7sT+m3Bysb6eg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz", + "integrity": "sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.7.4" + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.3" } }, "@babel/plugin-transform-parameters": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.7.4.tgz", - "integrity": "sha512-VJwhVePWPa0DqE9vcfptaJSzNDKrWU/4FbYCjZERtmqEs05g3UMXnYMZoXja7JAJ7Y7sPZipwm/pGApZt7wHlw==", + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.5.tgz", + "integrity": "sha512-0+1FhHnMfj6lIIhVvS4KGQJeuhe1GI//h5uptK4PvLt+BGBxsoUJbd3/IW002yk//6sZPlFgsG1hY6OHLcy6kA==", "dev": true, "requires": { - "@babel/helper-call-delegate": "^7.7.4", - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" - }, - "dependencies": { - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-property-literals": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.7.4.tgz", - "integrity": "sha512-MatJhlC4iHsIskWYyawl53KuHrt+kALSADLQQ/HkhTjX954fkxIEh4q5slL4oRAnsm/eDoZ4q0CIZpcqBuxhJQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz", + "integrity": "sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-regenerator": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.7.5.tgz", - "integrity": "sha512-/8I8tPvX2FkuEyWbjRCt4qTAgZK0DVy8QRguhA524UH48RfGJy94On2ri+dCuwOpcerPRl9O4ebQkRcVzIaGBw==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz", + "integrity": "sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA==", "dev": true, "requires": { - "regenerator-transform": "^0.14.0" + "regenerator-transform": "^0.14.2" } }, "@babel/plugin-transform-reserved-words": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.7.4.tgz", - "integrity": "sha512-OrPiUB5s5XvkCO1lS7D8ZtHcswIC57j62acAnJZKqGGnHP+TIc/ljQSrgdX/QyOTdEK5COAhuc820Hi1q2UgLQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz", + "integrity": "sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-runtime": { - "version": "7.7.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.7.6.tgz", - "integrity": "sha512-tajQY+YmXR7JjTwRvwL4HePqoL3DYxpYXIHKVvrOIvJmeHe2y1w4tz5qz9ObUDC9m76rCzIMPyn4eERuwA4a4A==", + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.9.6.tgz", + "integrity": "sha512-qcmiECD0mYOjOIt8YHNsAP1SxPooC/rDmfmiSK9BNY72EitdSc7l44WTEklaWuFtbOEBjNhWWyph/kOImbNJ4w==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", "resolve": "^1.8.1", "semver": "^5.5.1" }, @@ -1924,132 +940,160 @@ } }, "@babel/plugin-transform-shorthand-properties": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.7.4.tgz", - "integrity": "sha512-q+suddWRfIcnyG5YiDP58sT65AJDZSUhXQDZE3r04AuqD6d/XLaQPPXSBzP2zGerkgBivqtQm9XKGLuHqBID6Q==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz", + "integrity": "sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-spread": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.7.4.tgz", - "integrity": "sha512-8OSs0FLe5/80cndziPlg4R0K6HcWSM0zyNhHhLsmw/Nc5MaA49cAsnoJ/t/YZf8qkG7fD+UjTRaApVDB526d7Q==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz", + "integrity": "sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-sticky-regex": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.7.4.tgz", - "integrity": "sha512-Ls2NASyL6qtVe1H1hXts9yuEeONV2TJZmplLONkMPUG158CtmnrzW5Q5teibM5UVOFjG0D3IC5mzXR6pPpUY7A==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz", + "integrity": "sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-regex": "^7.8.3" } }, "@babel/plugin-transform-template-literals": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.7.4.tgz", - "integrity": "sha512-sA+KxLwF3QwGj5abMHkHgshp9+rRz+oY9uoRil4CyLtgEuE/88dpkeWgNk5qKVsJE9iSfly3nvHapdRiIS2wnQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz", + "integrity": "sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-typeof-symbol": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.7.4.tgz", - "integrity": "sha512-KQPUQ/7mqe2m0B8VecdyaW5XcQYaePyl9R7IsKd+irzj6jvbhoGnRE+M0aNkyAzI07VfUQ9266L5xMARitV3wg==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz", + "integrity": "sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-unicode-regex": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.7.4.tgz", - "integrity": "sha512-N77UUIV+WCvE+5yHw+oks3m18/umd7y392Zv7mYTpFqHtkpcc+QUz+gLJNTWVlWROIWeLqY0f3OjZxV5TcXnRw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz", + "integrity": "sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-create-regexp-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/preset-env": { - "version": "7.7.6", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.7.6.tgz", - "integrity": "sha512-k5hO17iF/Q7tR9Jv8PdNBZWYW6RofxhnxKjBMc0nG4JTaWvOTiPoO/RLFwAKcA4FpmuBFm6jkoqaRJLGi0zdaQ==", + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.9.6.tgz", + "integrity": "sha512-0gQJ9RTzO0heXOhzftog+a/WyOuqMrAIugVYxMYf83gh1CQaQDjMtsOpqOwXyDL/5JcWsrCm8l4ju8QC97O7EQ==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-async-generator-functions": "^7.7.4", - "@babel/plugin-proposal-dynamic-import": "^7.7.4", - "@babel/plugin-proposal-json-strings": "^7.7.4", - "@babel/plugin-proposal-object-rest-spread": "^7.7.4", - "@babel/plugin-proposal-optional-catch-binding": "^7.7.4", - "@babel/plugin-proposal-unicode-property-regex": "^7.7.4", - "@babel/plugin-syntax-async-generators": "^7.7.4", - "@babel/plugin-syntax-dynamic-import": "^7.7.4", - "@babel/plugin-syntax-json-strings": "^7.7.4", - "@babel/plugin-syntax-object-rest-spread": "^7.7.4", - "@babel/plugin-syntax-optional-catch-binding": "^7.7.4", - "@babel/plugin-syntax-top-level-await": "^7.7.4", - "@babel/plugin-transform-arrow-functions": "^7.7.4", - "@babel/plugin-transform-async-to-generator": "^7.7.4", - "@babel/plugin-transform-block-scoped-functions": "^7.7.4", - "@babel/plugin-transform-block-scoping": "^7.7.4", - "@babel/plugin-transform-classes": "^7.7.4", - "@babel/plugin-transform-computed-properties": "^7.7.4", - "@babel/plugin-transform-destructuring": "^7.7.4", - "@babel/plugin-transform-dotall-regex": "^7.7.4", - "@babel/plugin-transform-duplicate-keys": "^7.7.4", - "@babel/plugin-transform-exponentiation-operator": "^7.7.4", - "@babel/plugin-transform-for-of": "^7.7.4", - "@babel/plugin-transform-function-name": "^7.7.4", - "@babel/plugin-transform-literals": "^7.7.4", - "@babel/plugin-transform-member-expression-literals": "^7.7.4", - "@babel/plugin-transform-modules-amd": "^7.7.5", - "@babel/plugin-transform-modules-commonjs": "^7.7.5", - "@babel/plugin-transform-modules-systemjs": "^7.7.4", - "@babel/plugin-transform-modules-umd": "^7.7.4", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.7.4", - "@babel/plugin-transform-new-target": "^7.7.4", - "@babel/plugin-transform-object-super": "^7.7.4", - "@babel/plugin-transform-parameters": "^7.7.4", - "@babel/plugin-transform-property-literals": "^7.7.4", - "@babel/plugin-transform-regenerator": "^7.7.5", - "@babel/plugin-transform-reserved-words": "^7.7.4", - "@babel/plugin-transform-shorthand-properties": "^7.7.4", - "@babel/plugin-transform-spread": "^7.7.4", - "@babel/plugin-transform-sticky-regex": "^7.7.4", - "@babel/plugin-transform-template-literals": "^7.7.4", - "@babel/plugin-transform-typeof-symbol": "^7.7.4", - "@babel/plugin-transform-unicode-regex": "^7.7.4", - "@babel/types": "^7.7.4", - "browserslist": "^4.6.0", - "core-js-compat": "^3.4.7", + "@babel/compat-data": "^7.9.6", + "@babel/helper-compilation-targets": "^7.9.6", + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-proposal-async-generator-functions": "^7.8.3", + "@babel/plugin-proposal-dynamic-import": "^7.8.3", + "@babel/plugin-proposal-json-strings": "^7.8.3", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-proposal-numeric-separator": "^7.8.3", + "@babel/plugin-proposal-object-rest-spread": "^7.9.6", + "@babel/plugin-proposal-optional-catch-binding": "^7.8.3", + "@babel/plugin-proposal-optional-chaining": "^7.9.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.8.3", + "@babel/plugin-syntax-async-generators": "^7.8.0", + "@babel/plugin-syntax-dynamic-import": "^7.8.0", + "@babel/plugin-syntax-json-strings": "^7.8.0", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", + "@babel/plugin-syntax-numeric-separator": "^7.8.0", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.0", + "@babel/plugin-syntax-top-level-await": "^7.8.3", + "@babel/plugin-transform-arrow-functions": "^7.8.3", + "@babel/plugin-transform-async-to-generator": "^7.8.3", + "@babel/plugin-transform-block-scoped-functions": "^7.8.3", + "@babel/plugin-transform-block-scoping": "^7.8.3", + "@babel/plugin-transform-classes": "^7.9.5", + "@babel/plugin-transform-computed-properties": "^7.8.3", + "@babel/plugin-transform-destructuring": "^7.9.5", + "@babel/plugin-transform-dotall-regex": "^7.8.3", + "@babel/plugin-transform-duplicate-keys": "^7.8.3", + "@babel/plugin-transform-exponentiation-operator": "^7.8.3", + "@babel/plugin-transform-for-of": "^7.9.0", + "@babel/plugin-transform-function-name": "^7.8.3", + "@babel/plugin-transform-literals": "^7.8.3", + "@babel/plugin-transform-member-expression-literals": "^7.8.3", + "@babel/plugin-transform-modules-amd": "^7.9.6", + "@babel/plugin-transform-modules-commonjs": "^7.9.6", + "@babel/plugin-transform-modules-systemjs": "^7.9.6", + "@babel/plugin-transform-modules-umd": "^7.9.0", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.8.3", + "@babel/plugin-transform-new-target": "^7.8.3", + "@babel/plugin-transform-object-super": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.9.5", + "@babel/plugin-transform-property-literals": "^7.8.3", + "@babel/plugin-transform-regenerator": "^7.8.7", + "@babel/plugin-transform-reserved-words": "^7.8.3", + "@babel/plugin-transform-shorthand-properties": "^7.8.3", + "@babel/plugin-transform-spread": "^7.8.3", + "@babel/plugin-transform-sticky-regex": "^7.8.3", + "@babel/plugin-transform-template-literals": "^7.8.3", + "@babel/plugin-transform-typeof-symbol": "^7.8.4", + "@babel/plugin-transform-unicode-regex": "^7.8.3", + "@babel/preset-modules": "^0.1.3", + "@babel/types": "^7.9.6", + "browserslist": "^4.11.1", + "core-js-compat": "^3.6.2", "invariant": "^2.2.2", - "js-levenshtein": "^1.1.3", + "levenary": "^1.1.1", "semver": "^5.5.0" }, "dependencies": { - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", + "browserslist": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.12.0.tgz", + "integrity": "sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg==", "dev": true, "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" + "caniuse-lite": "^1.0.30001043", + "electron-to-chromium": "^1.3.413", + "node-releases": "^1.1.53", + "pkg-up": "^2.0.0" } }, + "caniuse-lite": { + "version": "1.0.30001051", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001051.tgz", + "integrity": "sha512-sw8UUnTlRevawTMZKN7vpfwSjCBVoiMPlYd8oT2VwNylyPCBdMAUmLGUApnYYTtIm5JXsQegUAY7GPHqgfDzjw==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.428", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.428.tgz", + "integrity": "sha512-u3+5jEfgLKq/hGO96YfAoOAM1tgFnRDTCD5mLuev44tttcXix+INtVegAkmGzUcfDsnzkPt51XXurXZLLwXt0w==", + "dev": true + }, + "node-releases": { + "version": "1.1.54", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.54.tgz", + "integrity": "sha512-tLzytKpgwKQr37yw9CEODjNM9lnmsNxzlv575GzOZ16AgMvPcJis/DgrJX4UEV1KIYoXk6XoVfY6YaMOPJESAQ==", + "dev": true + }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -2058,49 +1102,104 @@ } } }, - "@babel/runtime": { - "version": "7.7.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.6.tgz", - "integrity": "sha512-BWAJxpNVa0QlE5gZdWjSxXtemZyZ9RmrmVozxt3NUXeZhVIJ5ANyqmMc0JDrivBZyxUuQvFxlvH4OWWOogGfUw==", + "@babel/preset-modules": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.3.tgz", + "integrity": "sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg==", + "dev": true, "requires": { - "regenerator-runtime": "^0.13.2" + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/runtime": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.6.tgz", + "integrity": "sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ==", + "requires": { + "regenerator-runtime": "^0.13.4" }, "dependencies": { "regenerator-runtime": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", - "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==" + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" } } }, "@babel/template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", - "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4" + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", + "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.9.0", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + } } }, "@babel/traverse": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.5.tgz", - "integrity": "sha512-Vc+qjynwkjRmIFGxy0KYoPj4FdVDxLej89kMHFsWScq999uX+pwcX4v9mWRjW0KcAYTPAuVQl2LKP1wEVLsp+A==", + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.6.tgz", + "integrity": "sha512-b3rAHSjbxy6VEAvlxM8OV/0X4XrG72zoxme6q1MOoe2vd0bEc+TwayhuC1+Dfgqh1QEG+pj7atQqvUprHIccsg==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.4", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.4.5", - "@babel/types": "^7.4.4", + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.9.6", + "@babel/helper-function-name": "^7.9.5", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.9.6", + "@babel/types": "^7.9.6", "debug": "^4.1.0", "globals": "^11.1.0", - "lodash": "^4.17.11" + "lodash": "^4.17.13" }, "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", + "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.9.0", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -2111,38 +1210,38 @@ } }, "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true } } }, "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.6.tgz", + "integrity": "sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA==", "dev": true, "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", + "@babel/helper-validator-identifier": "^7.9.5", + "lodash": "^4.17.13", "to-fast-properties": "^2.0.0" } }, "@commitlint/cli": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-8.2.0.tgz", - "integrity": "sha512-8fJ5pmytc38yw2QWbTTJmXLfSiWPwMkHH4govo9zJ/+ERPBF2jvlxD/dQvk24ezcizjKc6LFka2edYC4OQ+Dgw==", + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-8.3.5.tgz", + "integrity": "sha512-6+L0vbw55UEdht71pgWOE55SRgb+8OHcEwGDB234VlIBFGK9P2QOBU7MHiYJ5cjdjCQ0rReNrGjOHmJ99jwf0w==", "dev": true, "requires": { - "@commitlint/format": "^8.2.0", - "@commitlint/lint": "^8.2.0", - "@commitlint/load": "^8.2.0", - "@commitlint/read": "^8.2.0", + "@commitlint/format": "^8.3.4", + "@commitlint/lint": "^8.3.5", + "@commitlint/load": "^8.3.5", + "@commitlint/read": "^8.3.4", "babel-polyfill": "6.26.0", "chalk": "2.4.2", "get-stdin": "7.0.0", - "lodash": "4.17.14", + "lodash": "4.17.15", "meow": "5.0.0", "resolve-from": "5.0.0", "resolve-global": "1.0.0" @@ -2196,12 +1295,6 @@ "path-exists": "^3.0.0" } }, - "lodash": { - "version": "4.17.14", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.14.tgz", - "integrity": "sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==", - "dev": true - }, "map-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", @@ -2318,116 +1411,92 @@ "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", "dev": true - }, - "yargs-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", - "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", - "dev": true, - "requires": { - "camelcase": "^4.1.0" - } } } }, "@commitlint/config-conventional": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-8.2.0.tgz", - "integrity": "sha512-HuwlHQ3DyVhpK9GHgTMhJXD8Zp8PGIQVpQGYh/iTrEU6TVxdRC61BxIDZvfWatCaiG617Z/U8maRAFrqFM4TqA==", - "dev": true - }, - "@commitlint/ensure": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-8.2.0.tgz", - "integrity": "sha512-XZZih/kcRrqK7lEORbSYCfqQw6byfsFbLygRGVdJMlCPGu9E2MjpwCtoj5z7y/lKfUB3MJaBhzn2muJqS1gC6A==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-8.3.4.tgz", + "integrity": "sha512-w0Yc5+aVAjZgjYqx29igBOnVCj8O22gy3Vo6Fyp7PwoS7+AYS1x3sN7IBq6i7Ae15Mv5P+rEx1pkxXo5zOMe4g==", "dev": true, "requires": { - "lodash": "4.17.14" + "conventional-changelog-conventionalcommits": "4.2.1" }, "dependencies": { - "lodash": { - "version": "4.17.14", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.14.tgz", - "integrity": "sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==", - "dev": true + "conventional-changelog-conventionalcommits": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.2.1.tgz", + "integrity": "sha512-vC02KucnkNNap+foDKFm7BVUSDAXktXrUJqGszUuYnt6T0J2azsbYz/w9TDc3VsrW2v6JOtiQWVcgZnporHr4Q==", + "dev": true, + "requires": { + "compare-func": "^1.3.1", + "lodash": "^4.2.1", + "q": "^1.5.1" + } } } }, + "@commitlint/ensure": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-8.3.4.tgz", + "integrity": "sha512-8NW77VxviLhD16O3EUd02lApMFnrHexq10YS4F4NftNoErKbKaJ0YYedktk2boKrtNRf/gQHY/Qf65edPx4ipw==", + "dev": true, + "requires": { + "lodash": "4.17.15" + } + }, "@commitlint/execute-rule": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-8.2.0.tgz", - "integrity": "sha512-9MBRthHaulbWTa8ReG2Oii2qc117NuvzhZdnkuKuYLhker7sUXGFcVhLanuWUKGyfyI2o9zVr/NHsNbCCsTzAA==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-8.3.4.tgz", + "integrity": "sha512-f4HigYjeIBn9f7OuNv5zh2y5vWaAhNFrfeul8CRJDy82l3Y+09lxOTGxfF3uMXKrZq4LmuK6qvvRCZ8mUrVvzQ==", "dev": true }, "@commitlint/format": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-8.2.0.tgz", - "integrity": "sha512-sA77agkDEMsEMrlGhrLtAg8vRexkOofEEv/CZX+4xlANyAz2kNwJvMg33lcL65CBhqKEnRRJRxfZ1ZqcujdKcQ==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-8.3.4.tgz", + "integrity": "sha512-809wlQ/ND6CLZON+w2Rb3YM2TLNDfU2xyyqpZeqzf2reJNpySMSUAeaO/fNDJSOKIsOsR3bI01rGu6hv28k+Nw==", "dev": true, "requires": { "chalk": "^2.0.1" } }, "@commitlint/is-ignored": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-8.2.0.tgz", - "integrity": "sha512-ADaGnKfbfV6KD1pETp0Qf7XAyc75xTy3WJlbvPbwZ4oPdBMsXF0oXEEGMis6qABfU2IXan5/KAJgAFX3vdd0jA==", + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-8.3.5.tgz", + "integrity": "sha512-Zo+8a6gJLFDTqyNRx53wQi/XTiz8mncvmWf/4oRG+6WRcBfjSSHY7KPVj5Y6UaLy2EgZ0WQ2Tt6RdTDeQiQplA==", "dev": true, "requires": { - "@types/semver": "^6.0.1", - "semver": "6.2.0" - }, - "dependencies": { - "semver": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.2.0.tgz", - "integrity": "sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A==", - "dev": true - } + "semver": "6.3.0" } }, "@commitlint/lint": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-8.2.0.tgz", - "integrity": "sha512-ch9JN8aR37ufdjoWv50jLfvFz9rWMgLW5HEkMGLsM/51gjekmQYS5NJg8S2+6F5+jmralAO7VkUMI6FukXKX0A==", + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-8.3.5.tgz", + "integrity": "sha512-02AkI0a6PU6rzqUvuDkSi6rDQ2hUgkq9GpmdJqfai5bDbxx2939mK4ZO+7apbIh4H6Pae7EpYi7ffxuJgm+3hQ==", "dev": true, "requires": { - "@commitlint/is-ignored": "^8.2.0", - "@commitlint/parse": "^8.2.0", - "@commitlint/rules": "^8.2.0", + "@commitlint/is-ignored": "^8.3.5", + "@commitlint/parse": "^8.3.4", + "@commitlint/rules": "^8.3.4", "babel-runtime": "^6.23.0", - "lodash": "4.17.14" - }, - "dependencies": { - "lodash": { - "version": "4.17.14", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.14.tgz", - "integrity": "sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==", - "dev": true - } + "lodash": "4.17.15" } }, "@commitlint/load": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-8.2.0.tgz", - "integrity": "sha512-EV6PfAY/p83QynNd1llHxJiNxKmp43g8+7dZbyfHFbsGOdokrCnoelAVZ+WGgktXwLN/uXyfkcIAxwac015UYw==", + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-8.3.5.tgz", + "integrity": "sha512-poF7R1CtQvIXRmVIe63FjSQmN9KDqjRtU5A6hxqXBga87yB2VUJzic85TV6PcQc+wStk52cjrMI+g0zFx+Zxrw==", "dev": true, "requires": { - "@commitlint/execute-rule": "^8.2.0", - "@commitlint/resolve-extends": "^8.2.0", + "@commitlint/execute-rule": "^8.3.4", + "@commitlint/resolve-extends": "^8.3.5", "babel-runtime": "^6.23.0", "chalk": "2.4.2", "cosmiconfig": "^5.2.0", - "lodash": "4.17.14", + "lodash": "4.17.15", "resolve-from": "^5.0.0" }, "dependencies": { - "lodash": { - "version": "4.17.14", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.14.tgz", - "integrity": "sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==", - "dev": true - }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -2437,53 +1506,46 @@ } }, "@commitlint/message": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-8.2.0.tgz", - "integrity": "sha512-LNsSwDLIFgE3nb/Sb1PIluYNy4Q8igdf4tpJCdv5JJDf7CZCZt3ZTglj0YutZZorpRRuHJsVIB2+dI4bVH3bFw==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-8.3.4.tgz", + "integrity": "sha512-nEj5tknoOKXqBsaQtCtgPcsAaf5VCg3+fWhss4Vmtq40633xLq0irkdDdMEsYIx8rGR0XPBTukqzln9kAWCkcA==", "dev": true }, "@commitlint/parse": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-8.2.0.tgz", - "integrity": "sha512-vzouqroTXG6QXApkrps0gbeSYW6w5drpUk7QAeZIcaCSPsQXDM8eqqt98ZzlzLJHo5oPNXPX1AAVSTrssvHemA==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-8.3.4.tgz", + "integrity": "sha512-b3uQvpUQWC20EBfKSfMRnyx5Wc4Cn778bVeVOFErF/cXQK725L1bYFvPnEjQO/GT8yGVzq2wtLaoEqjm1NJ/Bw==", "dev": true, "requires": { "conventional-changelog-angular": "^1.3.3", - "conventional-commits-parser": "^2.1.0", + "conventional-commits-parser": "^3.0.0", "lodash": "^4.17.11" } }, "@commitlint/read": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-8.2.0.tgz", - "integrity": "sha512-1tBai1VuSQmsOTsvJr3Fi/GZqX3zdxRqYe/yN4i3cLA5S2Y4QGJ5I3l6nGZlKgm/sSelTCVKHltrfWU8s5H7SA==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-8.3.4.tgz", + "integrity": "sha512-FKv1kHPrvcAG5j+OSbd41IWexsbLhfIXpxVC/YwQZO+FR0EHmygxQNYs66r+GnhD1EfYJYM4WQIqd5bJRx6OIw==", "dev": true, "requires": { - "@commitlint/top-level": "^8.2.0", + "@commitlint/top-level": "^8.3.4", "@marionebl/sander": "^0.6.0", "babel-runtime": "^6.23.0", - "git-raw-commits": "^1.3.0" + "git-raw-commits": "^2.0.0" } }, "@commitlint/resolve-extends": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-8.2.0.tgz", - "integrity": "sha512-cwi0HUsDcD502HBP8huXfTkVuWmeo1Fiz3GKxNwMBBsJV4+bKa7QrtxbNpXhVuarX7QjWfNTvmW6KmFS7YK9uw==", + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-8.3.5.tgz", + "integrity": "sha512-nHhFAK29qiXNe6oH6uG5wqBnCR+BQnxlBW/q5fjtxIaQALgfoNLHwLS9exzbIRFqwJckpR6yMCfgMbmbAOtklQ==", "dev": true, "requires": { - "@types/node": "^12.0.2", "import-fresh": "^3.0.0", - "lodash": "4.17.14", + "lodash": "4.17.15", "resolve-from": "^5.0.0", "resolve-global": "^1.0.0" }, "dependencies": { - "lodash": { - "version": "4.17.14", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.14.tgz", - "integrity": "sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==", - "dev": true - }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -2493,27 +1555,27 @@ } }, "@commitlint/rules": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-8.2.0.tgz", - "integrity": "sha512-FlqSBBP2Gxt5Ibw+bxdYpzqYR6HI8NIBpaTBhAjSEAduQtdWFMOhF0zsgkwH7lHN7opaLcnY2fXxAhbzTmJQQA==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-8.3.4.tgz", + "integrity": "sha512-xuC9dlqD5xgAoDFgnbs578cJySvwOSkMLQyZADb1xD5n7BNcUJfP8WjT9W1Aw8K3Wf8+Ym/ysr9FZHXInLeaRg==", "dev": true, "requires": { - "@commitlint/ensure": "^8.2.0", - "@commitlint/message": "^8.2.0", - "@commitlint/to-lines": "^8.2.0", + "@commitlint/ensure": "^8.3.4", + "@commitlint/message": "^8.3.4", + "@commitlint/to-lines": "^8.3.4", "babel-runtime": "^6.23.0" } }, "@commitlint/to-lines": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-8.2.0.tgz", - "integrity": "sha512-LXTYG3sMenlN5qwyTZ6czOULVcx46uMy+MEVqpvCgptqr/MZcV/C2J+S2o1DGwj1gOEFMpqrZaE3/1R2Q+N8ng==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-8.3.4.tgz", + "integrity": "sha512-5AvcdwRsMIVq0lrzXTwpbbG5fKRTWcHkhn/hCXJJ9pm1JidsnidS1y0RGkb3O50TEHGewhXwNoavxW9VToscUA==", "dev": true }, "@commitlint/top-level": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-8.2.0.tgz", - "integrity": "sha512-Yaw4KmYNy31/HhRUuZ+fupFcDalnfpdu4JGBgGAqS9aBHdMSSWdWqtAaDaxdtWjTZeN3O0sA2gOhXwvKwiDwvw==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-8.3.4.tgz", + "integrity": "sha512-nOaeLBbAqSZNpKgEtO6NAxmui1G8ZvLG+0wb4rvv6mWhPDzK1GNZkCd8FUZPahCoJ1iHDoatw7F8BbJLg4nDjg==", "dev": true, "requires": { "find-up": "^4.0.0" @@ -2575,9 +1637,9 @@ "dev": true }, "@fortawesome/fontawesome-pro": { - "version": "5.12.0", - "resolved": "https://npm.fontawesome.com/@fortawesome/fontawesome-pro/-/5.12.0/fontawesome-pro-5.12.0.tgz", - "integrity": "sha512-EKKR4p0higjsIPKjSSkGqtweUwo/GgR/zKL4rCwzF5Z/BZ/ebJZaS8ZjGE7YUNEN63SYk2WhpJVI+l9dwfU7RQ==", + "version": "5.13.0", + "resolved": "https://npm.fontawesome.com/@fortawesome/fontawesome-pro/-/5.13.0/fontawesome-pro-5.13.0.tgz", + "integrity": "sha512-97Su0vmIuCdxrklVbjTzY1i16nvyKaqFmFBgrTsgeCVmyIWXNydiS62jhKlkwH9sMb8Tw4wYQ+J1EKbRILowxw==", "dev": true }, "@juggle/resize-observer": { @@ -2594,6 +1656,23 @@ "graceful-fs": "^4.1.3", "mkdirp": "^0.5.1", "rimraf": "^2.5.2" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + } } }, "@samverschueren/stream-to-observable": { @@ -2605,6 +1684,51 @@ "any-observable": "^0.3.0" } }, + "@sindresorhus/df": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/df/-/df-2.1.0.tgz", + "integrity": "sha1-0gjPJ+BvC7R20U197M19cm6ao4k=", + "dev": true, + "requires": { + "execa": "^0.2.2" + }, + "dependencies": { + "execa": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.2.2.tgz", + "integrity": "sha1-4urUcsLDGq1vc/GslW7vReEjIMs=", + "dev": true, + "requires": { + "cross-spawn-async": "^2.1.1", + "npm-run-path": "^1.0.0", + "object-assign": "^4.0.1", + "path-key": "^1.0.0", + "strip-eof": "^1.0.0" + } + }, + "npm-run-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-1.0.0.tgz", + "integrity": "sha1-9cMr9ZX+ga6Sfa7FLoL4sACsPI8=", + "dev": true, + "requires": { + "path-key": "^1.0.0" + } + }, + "path-key": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-1.0.0.tgz", + "integrity": "sha1-XVPVeAGWRsDWiADbThRua9wqx68=", + "dev": true + } + } + }, + "@stroncium/procfs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@stroncium/procfs/-/procfs-1.2.1.tgz", + "integrity": "sha512-X1Iui3FUNZP18EUvysTHxt+Avu2nlVzyf90YM8OYgP6SGzTzzX/0JgObfO1AQQDzuZtNNz29bVh8h5R97JrjxA==", + "dev": true + }, "@types/anymatch": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz", @@ -2652,12 +1776,6 @@ "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==", "dev": true }, - "@types/semver": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-6.0.2.tgz", - "integrity": "sha512-G1Ggy7/9Nsa1Jt2yiBR2riEuyK2DFNnqow6R7cromXPMNynackRY1vqFTLz/gwnef1LHokbXThcPhqMRjUbkpQ==", - "dev": true - }, "@types/source-list-map": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", @@ -2729,178 +1847,177 @@ } }, "@webassemblyjs/ast": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", - "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", + "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", "dev": true, "requires": { - "@webassemblyjs/helper-module-context": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/wast-parser": "1.8.5" + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0" } }, "@webassemblyjs/floating-point-hex-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz", - "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", + "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==", "dev": true }, "@webassemblyjs/helper-api-error": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz", - "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", + "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", "dev": true }, "@webassemblyjs/helper-buffer": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz", - "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", + "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==", "dev": true }, "@webassemblyjs/helper-code-frame": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz", - "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", + "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", "dev": true, "requires": { - "@webassemblyjs/wast-printer": "1.8.5" + "@webassemblyjs/wast-printer": "1.9.0" } }, "@webassemblyjs/helper-fsm": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz", - "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", + "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==", "dev": true }, "@webassemblyjs/helper-module-context": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz", - "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", + "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.8.5", - "mamacro": "^0.0.3" + "@webassemblyjs/ast": "1.9.0" } }, "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz", - "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", + "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", "dev": true }, "@webassemblyjs/helper-wasm-section": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz", - "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", + "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5" + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0" } }, "@webassemblyjs/ieee754": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz", - "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", + "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", "dev": true, "requires": { "@xtuc/ieee754": "^1.2.0" } }, "@webassemblyjs/leb128": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz", - "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", + "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", "dev": true, "requires": { "@xtuc/long": "4.2.2" } }, "@webassemblyjs/utf8": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz", - "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", + "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", "dev": true }, "@webassemblyjs/wasm-edit": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz", - "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", + "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/helper-wasm-section": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5", - "@webassemblyjs/wasm-opt": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5", - "@webassemblyjs/wast-printer": "1.8.5" + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/helper-wasm-section": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-opt": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "@webassemblyjs/wast-printer": "1.9.0" } }, "@webassemblyjs/wasm-gen": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz", - "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", + "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/ieee754": "1.8.5", - "@webassemblyjs/leb128": "1.8.5", - "@webassemblyjs/utf8": "1.8.5" + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" } }, "@webassemblyjs/wasm-opt": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz", - "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", + "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5" + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0" } }, "@webassemblyjs/wasm-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz", - "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", + "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-api-error": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/ieee754": "1.8.5", - "@webassemblyjs/leb128": "1.8.5", - "@webassemblyjs/utf8": "1.8.5" + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" } }, "@webassemblyjs/wast-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz", - "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", + "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/floating-point-hex-parser": "1.8.5", - "@webassemblyjs/helper-api-error": "1.8.5", - "@webassemblyjs/helper-code-frame": "1.8.5", - "@webassemblyjs/helper-fsm": "1.8.5", + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/floating-point-hex-parser": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-code-frame": "1.9.0", + "@webassemblyjs/helper-fsm": "1.9.0", "@xtuc/long": "4.2.2" } }, "@webassemblyjs/wast-printer": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz", - "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", + "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/wast-parser": "1.8.5", + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0", "@xtuc/long": "4.2.2" } }, @@ -2937,9 +2054,9 @@ } }, "acorn": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", "dev": true }, "acorn-jsx": { @@ -3236,6 +2353,14 @@ "bn.js": "^4.0.0", "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + } } }, "assert": { @@ -3323,24 +2448,54 @@ "dev": true }, "autoprefixer": { - "version": "9.7.3", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.3.tgz", - "integrity": "sha512-8T5Y1C5Iyj6PgkPSFd0ODvK9DIleuPKUPYniNxybS47g2k2wFgLZ46lGQHlBuGKIAEV8fbCDfKCCRS1tvOgc3Q==", + "version": "9.7.6", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.6.tgz", + "integrity": "sha512-F7cYpbN7uVVhACZTeeIeealwdGM6wMtfWARVLTy5xmKtgVdBNJvbDRoCK3YO1orcs7gv/KwYlb3iXwu9Ug9BkQ==", "dev": true, "requires": { - "browserslist": "^4.8.0", - "caniuse-lite": "^1.0.30001012", + "browserslist": "^4.11.1", + "caniuse-lite": "^1.0.30001039", "chalk": "^2.4.2", "normalize-range": "^0.1.2", "num2fraction": "^1.2.2", - "postcss": "^7.0.23", - "postcss-value-parser": "^4.0.2" + "postcss": "^7.0.27", + "postcss-value-parser": "^4.0.3" }, "dependencies": { + "browserslist": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.12.0.tgz", + "integrity": "sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001043", + "electron-to-chromium": "^1.3.413", + "node-releases": "^1.1.53", + "pkg-up": "^2.0.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001051", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001051.tgz", + "integrity": "sha512-sw8UUnTlRevawTMZKN7vpfwSjCBVoiMPlYd8oT2VwNylyPCBdMAUmLGUApnYYTtIm5JXsQegUAY7GPHqgfDzjw==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.428", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.428.tgz", + "integrity": "sha512-u3+5jEfgLKq/hGO96YfAoOAM1tgFnRDTCD5mLuev44tttcXix+INtVegAkmGzUcfDsnzkPt51XXurXZLLwXt0w==", + "dev": true + }, + "node-releases": { + "version": "1.1.54", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.54.tgz", + "integrity": "sha512-tLzytKpgwKQr37yw9CEODjNM9lnmsNxzlv575GzOZ16AgMvPcJis/DgrJX4UEV1KIYoXk6XoVfY6YaMOPJESAQ==", + "dev": true + }, "postcss": { - "version": "7.0.24", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.24.tgz", - "integrity": "sha512-Xl0XvdNWg+CblAXzNvbSOUvgJXwSjmbAKORqyw9V2AlHrm1js2gFw9y3jibBAhpKZi8b5JzJCVh/FyzPsTtgTA==", + "version": "7.0.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.29.tgz", + "integrity": "sha512-ba0ApvR3LxGvRMMiUa9n0WR4HjzcYm7tS+ht4/2Nd0NLtHpPIH77fuB9Xh1/yJVz9O/E/95Y/dn8ygWsyffXtw==", "dev": true, "requires": { "chalk": "^2.4.2", @@ -3349,9 +2504,9 @@ } }, "postcss-value-parser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz", - "integrity": "sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", "dev": true }, "source-map": { @@ -3505,23 +2660,23 @@ } }, "babel-eslint": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.3.tgz", - "integrity": "sha512-z3U7eMY6r/3f3/JB9mTsLjyxrv0Yb1zb8PCWCLpguxfCzBIZUwy23R1t/XKewP+8mEN2Ck8Dtr4q20z6ce6SoA==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", + "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.0.0", - "@babel/traverse": "^7.0.0", - "@babel/types": "^7.0.0", + "@babel/parser": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0", "eslint-visitor-keys": "^1.0.0", "resolve": "^1.12.0" }, "dependencies": { "resolve": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", - "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", "dev": true, "requires": { "path-parse": "^1.0.6" @@ -3656,15 +2811,93 @@ } }, "babel-loader": { - "version": "8.0.6", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.6.tgz", - "integrity": "sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz", + "integrity": "sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw==", "dev": true, "requires": { - "find-cache-dir": "^2.0.0", - "loader-utils": "^1.0.2", - "mkdirp": "^0.5.1", - "pify": "^4.0.1" + "find-cache-dir": "^2.1.0", + "loader-utils": "^1.4.0", + "mkdirp": "^0.5.3", + "pify": "^4.0.1", + "schema-utils": "^2.6.5" + }, + "dependencies": { + "ajv": { + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", + "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", + "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "schema-utils": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.6.tgz", + "integrity": "sha512-wHutF/WPSbIi9x6ctjGGk2Hvl0VOz5l3EKEuKbjPlB30mKZUzb9A5k9yEXRX3pwyqVLPvpfZZEllaFq/M718hA==", + "dev": true, + "requires": { + "ajv": "^6.12.0", + "ajv-keywords": "^3.4.1" + } + } } }, "babel-messages": { @@ -3686,9 +2919,9 @@ } }, "babel-plugin-dynamic-import-node": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", - "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", "dev": true, "requires": { "object.assign": "^4.1.0" @@ -4029,9 +3262,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", - "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==", + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", + "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==", "dev": true }, "regenerator-runtime": { @@ -4094,6 +3327,21 @@ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==", "dev": true + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } } } }, @@ -4310,6 +3558,16 @@ "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", "dev": true }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, "blake2b": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/blake2b/-/blake2b-2.1.3.tgz", @@ -4340,9 +3598,9 @@ "dev": true }, "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-IUTD/REb78Z2eodka1QZyyEk66pciRcP6Sroka0aI3tG/iwIdYLrBD62RsubR7vqdt3WyX8p4jxeatzmRSphtA==", "dev": true }, "body-parser": { @@ -4459,21 +3717,49 @@ "requires": { "bn.js": "^4.1.0", "randombytes": "^2.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + } } }, "browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.1.0.tgz", + "integrity": "sha512-VYxo7cDCeYUoBZ0ZCy4UyEUCP3smyBd4DRQM5nrFS1jJjPJjX7rP3oLRpPoWfkhQfyJ0I9ZbHbKafrFD/SGlrg==", "dev": true, "requires": { - "bn.js": "^4.1.1", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.2", - "elliptic": "^6.0.0", - "inherits": "^2.0.1", - "parse-asn1": "^5.0.0" + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.2", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } } }, "browserify-zlib": { @@ -4560,37 +3846,28 @@ "dev": true }, "cacache": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-13.0.1.tgz", - "integrity": "sha512-5ZvAxd05HDDU+y9BVvcqYu2LLXmPnQ0hW62h32g4xBTgL/MppR4/04NHfj/ycM2y6lmTnbw6HVi+1eN0Psba6w==", + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", "dev": true, "requires": { - "chownr": "^1.1.2", + "bluebird": "^3.5.5", + "chownr": "^1.1.1", "figgy-pudding": "^3.5.1", - "fs-minipass": "^2.0.0", "glob": "^7.1.4", - "graceful-fs": "^4.2.2", - "infer-owner": "^1.0.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", "lru-cache": "^5.1.1", - "minipass": "^3.0.0", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", + "mississippi": "^3.0.0", "mkdirp": "^0.5.1", "move-concurrently": "^1.0.1", - "p-map": "^3.0.0", "promise-inflight": "^1.0.1", - "rimraf": "^2.7.1", - "ssri": "^7.0.0", - "unique-filename": "^1.1.1" + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" }, "dependencies": { - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - }, "lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -4600,32 +3877,19 @@ "yallist": "^3.0.2" } }, - "p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, "requires": { - "glob": "^7.1.3" - } - }, - "ssri": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-7.1.0.tgz", - "integrity": "sha512-77/WrDZUWocK0mvA5NTRQyveUf+wsrIc6vyrxpS8tVvYBcX215QbafrJR3KtkpskIzoFLqqNuuYQvxaMjXJ/0g==", - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1", - "minipass": "^3.1.1" + "minimist": "^1.2.5" } }, "yallist": { @@ -4708,19 +3972,26 @@ "dev": true }, "camelcase-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", + "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", "dev": true, "requires": { - "camelcase": "^2.0.0", - "map-obj": "^1.0.0" + "camelcase": "^4.1.0", + "map-obj": "^2.0.0", + "quick-lru": "^1.0.0" }, "dependencies": { "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "map-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", + "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", "dev": true } } @@ -4830,6 +4101,21 @@ "streamroller": "0.7.0" } }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -5031,9 +4317,9 @@ } }, "chownr": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.2.tgz", - "integrity": "sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "dev": true }, "chrome-trace-event": { @@ -5628,18 +4914,18 @@ } }, "conventional-changelog-core": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-3.2.2.tgz", - "integrity": "sha512-cssjAKajxaOX5LNAJLB+UOcoWjAIBvXtDMedv/58G+YEmAXMNfC16mmPl0JDOuVJVfIqM0nqQiZ8UCm8IXbE0g==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-3.2.3.tgz", + "integrity": "sha512-LMMX1JlxPIq/Ez5aYAYS5CpuwbOk6QFp8O4HLAcZxe3vxoCtABkhfjetk8IYdRB9CDQGwJFLR3Dr55Za6XKgUQ==", "dev": true, "requires": { - "conventional-changelog-writer": "^4.0.5", - "conventional-commits-parser": "^3.0.2", + "conventional-changelog-writer": "^4.0.6", + "conventional-commits-parser": "^3.0.3", "dateformat": "^3.0.0", "get-pkg-repo": "^1.0.0", "git-raw-commits": "2.0.0", "git-remote-origin-url": "^2.0.0", - "git-semver-tags": "^2.0.2", + "git-semver-tags": "^2.0.3", "lodash": "^4.2.1", "normalize-package-data": "^2.3.5", "q": "^1.5.1", @@ -5648,47 +4934,6 @@ "through2": "^3.0.0" }, "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "camelcase-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", - "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", - "dev": true, - "requires": { - "camelcase": "^4.1.0", - "map-obj": "^2.0.0", - "quick-lru": "^1.0.0" - } - }, - "conventional-commits-parser": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.0.3.tgz", - "integrity": "sha512-KaA/2EeUkO4bKjinNfGUyqPTX/6w9JGshuQRik4r/wJz7rUw3+D3fDG6sZSEqJvKILzKXFQuFkpPLclcsAuZcg==", - "dev": true, - "requires": { - "JSONStream": "^1.0.4", - "is-text-path": "^2.0.0", - "lodash": "^4.2.1", - "meow": "^4.0.0", - "split2": "^2.0.0", - "through2": "^3.0.0", - "trim-off-newlines": "^1.0.0" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, "git-raw-commits": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.0.tgz", @@ -5714,43 +4959,16 @@ } } }, - "is-text-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", - "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", + "git-semver-tags": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-2.0.3.tgz", + "integrity": "sha512-tj4FD4ww2RX2ae//jSrXZzrocla9db5h0V7ikPl1P/WwoZar9epdUhwR7XHXSgc+ZkNq72BEEerqQuicoEQfzA==", "dev": true, "requires": { - "text-extensions": "^2.0.0" + "meow": "^4.0.0", + "semver": "^6.0.0" } }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "map-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", - "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", - "dev": true - }, "meow": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", @@ -5769,48 +4987,9 @@ } }, "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, "read-pkg": { @@ -5824,44 +5003,6 @@ "path-type": "^3.0.0" } }, - "read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - } - }, - "redent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", - "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", - "dev": true, - "requires": { - "indent-string": "^3.0.0", - "strip-indent": "^2.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "strip-indent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", - "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", - "dev": true - }, - "text-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.0.0.tgz", - "integrity": "sha512-F91ZqLgvi1E0PdvmxMgp+gcf6q8fMH7mhdwWfzXnl1k+GbpQDmi8l7DzLC5JTASKbwpY3TfxajAUzAXcv2NmsQ==", - "dev": true - }, "through2": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", @@ -5870,12 +5011,6 @@ "requires": { "readable-stream": "2 || 3" } - }, - "trim-newlines": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", - "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", - "dev": true } } }, @@ -5932,188 +5067,23 @@ "dev": true }, "conventional-changelog-writer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.6.tgz", - "integrity": "sha512-ou/sbrplJMM6KQpR5rKFYNVQYesFjN7WpNGdudQSWNi6X+RgyFUcSv871YBYkrUYV9EX8ijMohYVzn9RUb+4ag==", + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.11.tgz", + "integrity": "sha512-g81GQOR392I+57Cw3IyP1f+f42ME6aEkbR+L7v1FBBWolB0xkjKTeCWVguzRrp6UiT1O6gBpJbEy2eq7AnV1rw==", "dev": true, "requires": { "compare-func": "^1.3.1", "conventional-commits-filter": "^2.0.2", "dateformat": "^3.0.0", - "handlebars": "^4.1.0", + "handlebars": "^4.4.0", "json-stringify-safe": "^5.0.1", - "lodash": "^4.2.1", - "meow": "^4.0.0", + "lodash": "^4.17.15", + "meow": "^5.0.0", "semver": "^6.0.0", "split": "^1.0.0", "through2": "^3.0.0" }, "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "camelcase-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", - "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", - "dev": true, - "requires": { - "camelcase": "^4.1.0", - "map-obj": "^2.0.0", - "quick-lru": "^1.0.0" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "map-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", - "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", - "dev": true - }, - "meow": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", - "integrity": "sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A==", - "dev": true, - "requires": { - "camelcase-keys": "^4.0.0", - "decamelize-keys": "^1.0.0", - "loud-rejection": "^1.0.0", - "minimist": "^1.1.3", - "minimist-options": "^3.0.1", - "normalize-package-data": "^2.3.4", - "read-pkg-up": "^3.0.0", - "redent": "^2.0.0", - "trim-newlines": "^2.0.0" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - } - }, - "redent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", - "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", - "dev": true, - "requires": { - "indent-string": "^3.0.0", - "strip-indent": "^2.0.0" - } - }, - "semver": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.1.tgz", - "integrity": "sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==", - "dev": true - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "strip-indent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", - "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", - "dev": true - }, "through2": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", @@ -6122,12 +5092,6 @@ "requires": { "readable-stream": "2 || 3" } - }, - "trim-newlines": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", - "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", - "dev": true } } }, @@ -6142,17 +5106,17 @@ } }, "conventional-commits-parser": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-2.1.7.tgz", - "integrity": "sha512-BoMaddIEJ6B4QVMSDu9IkVImlGOSGA1I2BQyOZHeLQ6qVOJLcLKn97+fL6dGbzWEiqDzfH4OkcveULmeq2MHFQ==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.0.8.tgz", + "integrity": "sha512-YcBSGkZbYp7d+Cr3NWUeXbPDFUN6g3SaSIzOybi8bjHL5IJ5225OSCxJJ4LgziyEJ7AaJtE9L2/EU6H7Nt/DDQ==", "dev": true, "requires": { "JSONStream": "^1.0.4", - "is-text-path": "^1.0.0", - "lodash": "^4.2.1", - "meow": "^4.0.0", + "is-text-path": "^1.0.1", + "lodash": "^4.17.15", + "meow": "^5.0.0", "split2": "^2.0.0", - "through2": "^2.0.0", + "through2": "^3.0.0", "trim-off-newlines": "^1.0.0" }, "dependencies": { @@ -6211,28 +5175,22 @@ "dev": true }, "meow": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", - "integrity": "sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-5.0.0.tgz", + "integrity": "sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig==", "dev": true, "requires": { "camelcase-keys": "^4.0.0", "decamelize-keys": "^1.0.0", "loud-rejection": "^1.0.0", - "minimist": "^1.1.3", "minimist-options": "^3.0.1", "normalize-package-data": "^2.3.4", "read-pkg-up": "^3.0.0", "redent": "^2.0.0", - "trim-newlines": "^2.0.0" + "trim-newlines": "^2.0.0", + "yargs-parser": "^10.0.0" } }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, "p-limit": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", @@ -6315,6 +5273,15 @@ "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", "dev": true }, + "through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "dev": true, + "requires": { + "readable-stream": "2 || 3" + } + }, "trim-newlines": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", @@ -6487,9 +5454,9 @@ } }, "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, "p-limit": { @@ -6641,6 +5608,23 @@ "mkdirp": "^0.5.1", "rimraf": "^2.5.4", "run-queue": "^1.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + } } }, "copy-descriptor": { @@ -6650,9 +5634,9 @@ "dev": true }, "copy-webpack-plugin": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.1.0.tgz", - "integrity": "sha512-0sNrj/Sx7/cWA0k7CVQa0sdA/dzCybqSb0+GbhKuQdOlAvnAwgC2osmbAFOAfha7ZXnreoQmCq5oDjG3gP4VHw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.1.1.tgz", + "integrity": "sha512-P15M5ZC8dyCjQHWwd4Ia/dm0SgVvZJMYeykVIVYXbGyqO4dWB5oyPHp9i7wjwo5LhtlhKbiBCdS2NvM07Wlybg==", "dev": true, "requires": { "cacache": "^12.0.3", @@ -6669,29 +5653,6 @@ "webpack-log": "^2.0.0" }, "dependencies": { - "cacache": { - "version": "12.0.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", - "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", - "dev": true, - "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - } - }, "globby": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", @@ -6712,19 +5673,10 @@ "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", "dev": true }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -6736,12 +5688,6 @@ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true }, - "serialize-javascript": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", - "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", - "dev": true - }, "slash": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", @@ -6757,40 +5703,58 @@ "ansi-colors": "^3.0.0", "uuid": "^3.3.2" } - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true } } }, "core-js": { - "version": "3.4.8", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.4.8.tgz", - "integrity": "sha512-b+BBmCZmVgho8KnBUOXpvlqEMguko+0P+kXCwD4vIprsXC6ht1qgPxtb1OK6XgSlrySF71wkwBQ0Hv695bk9gQ==" + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", + "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==" }, "core-js-compat": { - "version": "3.4.8", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.4.8.tgz", - "integrity": "sha512-l3WTmnXHV2Sfu5VuD7EHE2w7y+K68+kULKt5RJg8ZJk3YhHF1qLD4O8v8AmNq+8vbOwnPFFDvds25/AoEvMqlQ==", + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz", + "integrity": "sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==", "dev": true, "requires": { - "browserslist": "^4.8.2", - "semver": "^6.3.0" + "browserslist": "^4.8.5", + "semver": "7.0.0" }, "dependencies": { + "browserslist": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.12.0.tgz", + "integrity": "sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001043", + "electron-to-chromium": "^1.3.413", + "node-releases": "^1.1.53", + "pkg-up": "^2.0.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001051", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001051.tgz", + "integrity": "sha512-sw8UUnTlRevawTMZKN7vpfwSjCBVoiMPlYd8oT2VwNylyPCBdMAUmLGUApnYYTtIm5JXsQegUAY7GPHqgfDzjw==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.428", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.428.tgz", + "integrity": "sha512-u3+5jEfgLKq/hGO96YfAoOAM1tgFnRDTCD5mLuev44tttcXix+INtVegAkmGzUcfDsnzkPt51XXurXZLLwXt0w==", + "dev": true + }, + "node-releases": { + "version": "1.1.54", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.54.tgz", + "integrity": "sha512-tLzytKpgwKQr37yw9CEODjNM9lnmsNxzlv575GzOZ16AgMvPcJis/DgrJX4UEV1KIYoXk6XoVfY6YaMOPJESAQ==", + "dev": true + }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", "dev": true } } @@ -6831,6 +5795,19 @@ } } }, + "cp-file": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-6.2.0.tgz", + "integrity": "sha512-fmvV4caBnofhPe8kOcitBwSn2f39QLjnAnGq3gO9dfd75mUytzKNZB1hde6QHunW2Rt+OwuBOMc3i1tNElbszA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "make-dir": "^2.0.0", + "nested-error-stacks": "^2.0.0", + "pify": "^4.0.1", + "safe-buffer": "^5.0.1" + } + }, "create-ecdh": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", @@ -6839,6 +5816,14 @@ "requires": { "bn.js": "^4.1.0", "elliptic": "^6.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + } } }, "create-hash": { @@ -6889,6 +5874,16 @@ } } }, + "cross-spawn-async": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/cross-spawn-async/-/cross-spawn-async-2.2.5.tgz", + "integrity": "sha1-hF/wwINKPe2dFg2sptOQkGuyiMw=", + "dev": true, + "requires": { + "lru-cache": "^4.0.0", + "which": "^1.2.8" + } + }, "crypto-browserify": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", @@ -7172,9 +6167,9 @@ "dev": true }, "cyclist": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", - "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", "dev": true }, "d": { @@ -7420,6 +6415,14 @@ "bn.js": "^4.1.0", "miller-rabin": "^4.0.0", "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + } } }, "dir-glob": { @@ -7599,6 +6602,14 @@ "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0", "minimalistic-crypto-utils": "^1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + } } }, "emoji-regex": { @@ -7980,6 +6991,21 @@ "estraverse": "^4.1.1" } }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", @@ -8098,9 +7124,9 @@ "dev": true }, "events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", - "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", + "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==", "dev": true }, "evp_bytestokey": { @@ -8470,9 +7496,9 @@ "dev": true }, "figgy-pudding": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", - "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", "dev": true }, "figures": { @@ -8494,22 +7520,22 @@ } }, "file-loader": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-5.0.2.tgz", - "integrity": "sha512-QMiQ+WBkGLejKe81HU8SZ9PovsU/5uaLo0JdTCEXOYv7i7jfAjHZi1tcwp9tSASJPOmmHZtbdCervFmXMH/Dcg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-5.1.0.tgz", + "integrity": "sha512-u/VkLGskw3Ue59nyOwUwXI/6nuBCo7KBkniB/l7ICwr/7cPNGsL1WCXUp3GB0qgOOKU1TiP49bv4DZF/LJqprg==", "dev": true, "requires": { - "loader-utils": "^1.2.3", + "loader-utils": "^1.4.0", "schema-utils": "^2.5.0" }, "dependencies": { "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", + "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", "dev": true, "requires": { - "fast-deep-equal": "^2.0.1", + "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" @@ -8521,18 +7547,63 @@ "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", "dev": true }, - "schema-utils": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.1.tgz", - "integrity": "sha512-0WXHDs1VDJyo+Zqs9TKLKyD/h7yDpHUhEFsM2CzkICFdoX1av+GBq/J2xRTFfsQO5kBfhZzANf2VcIm84jqDbg==", + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "dev": true, "requires": { - "ajv": "^6.10.2", + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "schema-utils": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.6.tgz", + "integrity": "sha512-wHutF/WPSbIi9x6ctjGGk2Hvl0VOz5l3EKEuKbjPlB30mKZUzb9A5k9yEXRX3pwyqVLPvpfZZEllaFq/M718hA==", + "dev": true, + "requires": { + "ajv": "^6.12.0", "ajv-keywords": "^3.4.1" } } } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -8751,9 +7822,9 @@ } }, "fs-minipass": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.0.0.tgz", - "integrity": "sha512-40Qz+LFXmd9tzYVnnBmZvFfvAADfUA14TXPK1s7IfElJTIZ97rA8w4Kin7Wt5JBrC3ShnnFJO/5vPjPEeJIq9A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "dev": true, "requires": { "minipass": "^3.0.0" @@ -8784,14 +7855,15 @@ "dev": true }, "fsevents": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", - "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz", + "integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==", "dev": true, "optional": true, "requires": { + "bindings": "^1.5.0", "nan": "^2.12.1", - "node-pre-gyp": "^0.12.0" + "node-pre-gyp": "*" }, "dependencies": { "abbrev": { @@ -8839,7 +7911,7 @@ } }, "chownr": { - "version": "1.1.1", + "version": "1.1.4", "bundled": true, "dev": true, "optional": true @@ -8869,7 +7941,7 @@ "optional": true }, "debug": { - "version": "4.1.1", + "version": "3.2.6", "bundled": true, "dev": true, "optional": true, @@ -8896,12 +7968,12 @@ "optional": true }, "fs-minipass": { - "version": "1.2.5", + "version": "1.2.7", "bundled": true, "dev": true, "optional": true, "requires": { - "minipass": "^2.2.1" + "minipass": "^2.6.0" } }, "fs.realpath": { @@ -8927,7 +7999,7 @@ } }, "glob": { - "version": "7.1.3", + "version": "7.1.6", "bundled": true, "dev": true, "optional": true, @@ -8956,7 +8028,7 @@ } }, "ignore-walk": { - "version": "3.0.1", + "version": "3.0.3", "bundled": true, "dev": true, "optional": true, @@ -8975,7 +8047,7 @@ } }, "inherits": { - "version": "2.0.3", + "version": "2.0.4", "bundled": true, "dev": true, "optional": true @@ -9011,13 +8083,13 @@ } }, "minimist": { - "version": "0.0.8", + "version": "1.2.5", "bundled": true, "dev": true, "optional": true }, "minipass": { - "version": "2.3.5", + "version": "2.9.0", "bundled": true, "dev": true, "optional": true, @@ -9027,42 +8099,42 @@ } }, "minizlib": { - "version": "1.2.1", + "version": "1.3.3", "bundled": true, "dev": true, "optional": true, "requires": { - "minipass": "^2.2.1" + "minipass": "^2.9.0" } }, "mkdirp": { - "version": "0.5.1", + "version": "0.5.3", "bundled": true, "dev": true, "optional": true, "requires": { - "minimist": "0.0.8" + "minimist": "^1.2.5" } }, "ms": { - "version": "2.1.1", + "version": "2.1.2", "bundled": true, "dev": true, "optional": true }, "needle": { - "version": "2.3.0", + "version": "2.3.3", "bundled": true, "dev": true, "optional": true, "requires": { - "debug": "^4.1.0", + "debug": "^3.2.6", "iconv-lite": "^0.4.4", "sax": "^1.2.4" } }, "node-pre-gyp": { - "version": "0.12.0", + "version": "0.14.0", "bundled": true, "dev": true, "optional": true, @@ -9076,11 +8148,11 @@ "rc": "^1.2.7", "rimraf": "^2.6.1", "semver": "^5.3.0", - "tar": "^4" + "tar": "^4.4.2" } }, "nopt": { - "version": "4.0.1", + "version": "4.0.3", "bundled": true, "dev": true, "optional": true, @@ -9090,19 +8162,29 @@ } }, "npm-bundled": { - "version": "1.0.6", + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", "bundled": true, "dev": true, "optional": true }, "npm-packlist": { - "version": "1.4.1", + "version": "1.4.8", "bundled": true, "dev": true, "optional": true, "requires": { "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" } }, "npmlog": { @@ -9167,7 +8249,7 @@ "optional": true }, "process-nextick-args": { - "version": "2.0.0", + "version": "2.0.1", "bundled": true, "dev": true, "optional": true @@ -9182,18 +8264,10 @@ "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } } }, "readable-stream": { - "version": "2.3.6", + "version": "2.3.7", "bundled": true, "dev": true, "optional": true, @@ -9208,7 +8282,7 @@ } }, "rimraf": { - "version": "2.6.3", + "version": "2.7.1", "bundled": true, "dev": true, "optional": true, @@ -9235,7 +8309,7 @@ "optional": true }, "semver": { - "version": "5.7.0", + "version": "5.7.1", "bundled": true, "dev": true, "optional": true @@ -9288,18 +8362,18 @@ "optional": true }, "tar": { - "version": "4.4.8", + "version": "4.4.13", "bundled": true, "dev": true, "optional": true, "requires": { "chownr": "^1.1.1", "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", "mkdirp": "^0.5.0", "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" + "yallist": "^3.0.3" } }, "util-deprecate": { @@ -9324,7 +8398,7 @@ "optional": true }, "yallist": { - "version": "3.0.3", + "version": "3.1.1", "bundled": true, "dev": true, "optional": true @@ -9341,6 +8415,23 @@ "inherits": "~2.0.0", "mkdirp": ">=0.5 0", "rimraf": "2" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + } } }, "function-bind": { @@ -9366,6 +8457,12 @@ "simple-git": "^1.85.0" } }, + "gensync": { + "version": "1.0.0-beta.1", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", + "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", + "dev": true + }, "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", @@ -9389,6 +8486,176 @@ "normalize-package-data": "^2.3.0", "parse-github-repo-url": "^1.3.0", "through2": "^2.0.0" + }, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + } } }, "get-stdin": { @@ -9422,16 +8689,16 @@ } }, "git-raw-commits": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-1.3.6.tgz", - "integrity": "sha512-svsK26tQ8vEKnMshTDatSIQSMDdz8CxIIqKsvPqbtV23Etmw6VNaFAitu8zwZ0VrOne7FztwPyRLxK7/DIUTQg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.3.tgz", + "integrity": "sha512-SoSsFL5lnixVzctGEi2uykjA7B5I0AhO9x6kdzvGRHbxsa6JSEgrgy1esRKsfOKE1cgyOJ/KDR2Trxu157sb8w==", "dev": true, "requires": { "dargs": "^4.0.1", "lodash.template": "^4.0.2", - "meow": "^4.0.0", + "meow": "^5.0.0", "split2": "^2.0.0", - "through2": "^2.0.0" + "through2": "^3.0.0" }, "dependencies": { "camelcase": { @@ -9489,28 +8756,22 @@ "dev": true }, "meow": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", - "integrity": "sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-5.0.0.tgz", + "integrity": "sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig==", "dev": true, "requires": { "camelcase-keys": "^4.0.0", "decamelize-keys": "^1.0.0", "loud-rejection": "^1.0.0", - "minimist": "^1.1.3", "minimist-options": "^3.0.1", "normalize-package-data": "^2.3.4", "read-pkg-up": "^3.0.0", "redent": "^2.0.0", - "trim-newlines": "^2.0.0" + "trim-newlines": "^2.0.0", + "yargs-parser": "^10.0.0" } }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, "p-limit": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", @@ -9593,6 +8854,15 @@ "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", "dev": true }, + "through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "dev": true, + "requires": { + "readable-stream": "2 || 3" + } + }, "trim-newlines": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", @@ -9701,9 +8971,9 @@ } }, "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, "p-limit": { @@ -9924,17 +9194,24 @@ "dev": true }, "handlebars": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz", - "integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==", + "version": "4.7.6", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz", + "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==", "dev": true, "requires": { + "minimist": "^1.2.5", "neo-async": "^2.6.0", - "optimist": "^0.6.1", "source-map": "^0.6.1", - "uglify-js": "^3.1.4" + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" }, "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -10045,13 +9322,39 @@ } }, "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", "dev": true, "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true + } } }, "hash.js": { @@ -10968,22 +10271,28 @@ "dev": true }, "jest-worker": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", - "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.5.0.tgz", + "integrity": "sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw==", "dev": true, "requires": { "merge-stream": "^2.0.0", - "supports-color": "^6.1.0" + "supports-color": "^7.0.0" }, "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" } } } @@ -10993,12 +10302,6 @@ "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==" }, - "js-levenshtein": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", - "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", - "dev": true - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -11058,18 +10361,18 @@ "dev": true }, "json5": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", - "integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", "dev": true, "requires": { - "minimist": "^1.2.0" + "minimist": "^1.2.5" }, "dependencies": { "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true } } @@ -11286,9 +10589,9 @@ } }, "karma-jasmine-html-reporter": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.4.2.tgz", - "integrity": "sha512-7g0gPj8+9JepCNJR9WjDyQ2RkZ375jpdurYQyAYv8PorUCadepl8vrD6LmMqOGcM17cnrynBawQYZHaumgDjBw==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.5.3.tgz", + "integrity": "sha512-ci0VrjuCaFj+9d1tYlTE3KIPUCp0rz874zWWU3JgCMqGIyw5ke+BXWFPOAGAqUdCJcrMwneyvp1zFXA74MiPUA==", "dev": true }, "karma-mocha-reporter": { @@ -11334,9 +10637,9 @@ } }, "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, "last-call-webpack-plugin": { @@ -11358,6 +10661,21 @@ "invert-kv": "^1.0.0" } }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "levenary": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz", + "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==", + "dev": true, + "requires": { + "leven": "^3.1.0" + } + }, "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -11535,31 +10853,21 @@ } }, "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", "dev": true, "requires": { "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" }, "dependencies": { - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - }, "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true } } @@ -11591,9 +10899,9 @@ } }, "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true } } @@ -11862,12 +11170,6 @@ } } }, - "mamacro": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", - "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==", - "dev": true - }, "map-age-cleaner": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", @@ -12034,29 +11336,20 @@ "dev": true }, "meow": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", - "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-5.0.0.tgz", + "integrity": "sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig==", "dev": true, "requires": { - "camelcase-keys": "^2.0.0", - "decamelize": "^1.1.2", + "camelcase-keys": "^4.0.0", + "decamelize-keys": "^1.0.0", "loud-rejection": "^1.0.0", - "map-obj": "^1.0.1", - "minimist": "^1.1.3", + "minimist-options": "^3.0.1", "normalize-package-data": "^2.3.4", - "object-assign": "^4.0.1", - "read-pkg-up": "^1.0.1", - "redent": "^1.0.0", - "trim-newlines": "^1.0.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } + "read-pkg-up": "^3.0.0", + "redent": "^2.0.0", + "trim-newlines": "^2.0.0", + "yargs-parser": "^10.0.0" } }, "merge-descriptors": { @@ -12118,6 +11411,14 @@ "requires": { "bn.js": "^4.0.0", "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + } } }, "mime": { @@ -12148,9 +11449,9 @@ "dev": true }, "mini-css-extract-plugin": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.8.0.tgz", - "integrity": "sha512-MNpRGbNA52q6U92i0qbVpQNsgk7LExy41MdAlG84FeytfDOtRIf/mCHdEgG8rpTKOaNKiqUnZdlptF469hxqOw==", + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.8.2.tgz", + "integrity": "sha512-a3Y4of27Wz+mqK3qrcd3VhYz6cU0iW5x3Sgvqzbj+XmlrSizmvu8QQMl5oMYJjgHOC4iyt+w7l4umP+dQeW3bw==", "dev": true, "requires": { "loader-utils": "^1.1.0", @@ -12280,13 +11581,10 @@ } }, "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true }, "modify-values": { "version": "1.0.1", @@ -12295,9 +11593,34 @@ "dev": true }, "moment": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" + "version": "2.25.3", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.25.3.tgz", + "integrity": "sha512-PuYv0PHxZvzc15Sp8ybUCoQ+xpyPWvjOuK72a5ovzp2LI32rJXOiIfyoFoYvG3s6EwwrdkMyWuRiEHSZRLJNdg==" + }, + "mount-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mount-point/-/mount-point-3.0.0.tgz", + "integrity": "sha1-Zly57evoDREOZY21bDHQrvUaj5c=", + "dev": true, + "requires": { + "@sindresorhus/df": "^1.0.1", + "pify": "^2.3.0", + "pinkie-promise": "^2.0.1" + }, + "dependencies": { + "@sindresorhus/df": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@sindresorhus/df/-/df-1.0.1.tgz", + "integrity": "sha1-xptm9S9vzdKHyAffIQMF2694UA0=", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } }, "move-concurrently": { "version": "1.0.1", @@ -12311,6 +11634,45 @@ "mkdirp": "^0.5.1", "rimraf": "^2.5.4", "run-queue": "^1.0.3" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + } + } + }, + "move-file": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/move-file/-/move-file-1.2.0.tgz", + "integrity": "sha512-USHrRmxzGowUWAGBbJPdFjHzEqtxDU03pLHY0Rfqgtnq+q8FOIs8wvkkf+Udmg77SJKs47y9sI0jJvQeYsmiCA==", + "dev": true, + "requires": { + "cp-file": "^6.1.0", + "make-dir": "^3.0.0", + "path-exists": "^3.0.0" + }, + "dependencies": { + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + } } }, "ms": { @@ -12326,9 +11688,9 @@ "dev": true }, "nan": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", + "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", "dev": true, "optional": true }, @@ -12374,6 +11736,12 @@ "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", "dev": true }, + "nested-error-stacks": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz", + "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==", + "dev": true + }, "netmask": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/netmask/-/netmask-1.0.6.tgz", @@ -12542,9 +11910,9 @@ } }, "npm": { - "version": "6.13.7", - "resolved": "https://registry.npmjs.org/npm/-/npm-6.13.7.tgz", - "integrity": "sha512-X967EKTT407CvgrWFjXusnPh0VLERcmR9hZFSVgkEquOomZkvpwLJ5zrQ3qrG9SpPLKJE4bPLUu76exKQ4a3Cg==", + "version": "6.14.5", + "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.5.tgz", + "integrity": "sha512-CDwa3FJd0XJpKDbWCST484H+mCNjF26dPrU+xnREW+upR0UODjMEfXPl3bxWuAwZIX6c2ASg1plLO7jP8ehWeA==", "requires": { "JSONStream": "^1.3.5", "abbrev": "~1.1.1", @@ -12557,7 +11925,7 @@ "byte-size": "^5.0.1", "cacache": "^12.0.3", "call-limit": "^1.1.1", - "chownr": "^1.1.3", + "chownr": "^1.1.4", "ci-info": "^2.0.0", "cli-columns": "^3.1.2", "cli-table3": "^0.5.1", @@ -12574,10 +11942,10 @@ "fs-vacuum": "~1.2.10", "fs-write-stream-atomic": "~1.0.10", "gentle-fs": "^2.3.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.3", + "glob": "^7.1.6", + "graceful-fs": "^4.2.4", "has-unicode": "~2.0.1", - "hosted-git-info": "^2.8.5", + "hosted-git-info": "^2.8.8", "iferr": "^1.0.2", "imurmurhash": "*", "infer-owner": "^1.0.4", @@ -12612,20 +11980,20 @@ "lru-cache": "^5.1.1", "meant": "~1.0.1", "mississippi": "^3.0.0", - "mkdirp": "~0.5.1", + "mkdirp": "^0.5.5", "move-concurrently": "^1.0.1", - "node-gyp": "^5.0.7", - "nopt": "~4.0.1", + "node-gyp": "^5.1.0", + "nopt": "^4.0.3", "normalize-package-data": "^2.5.0", "npm-audit-report": "^1.3.2", "npm-cache-filename": "~1.0.2", "npm-install-checks": "^3.0.2", "npm-lifecycle": "^3.1.4", "npm-package-arg": "^6.1.1", - "npm-packlist": "^1.4.7", + "npm-packlist": "^1.4.8", "npm-pick-manifest": "^3.0.2", - "npm-profile": "^4.0.2", - "npm-registry-fetch": "^4.0.2", + "npm-profile": "^4.0.4", + "npm-registry-fetch": "^4.0.4", "npm-user-validate": "~1.0.0", "npmlog": "~4.1.2", "once": "~1.4.0", @@ -12642,11 +12010,11 @@ "read-installed": "~4.0.3", "read-package-json": "^2.1.1", "read-package-tree": "^5.3.1", - "readable-stream": "^3.4.0", + "readable-stream": "^3.6.0", "readdir-scoped-modules": "^1.1.0", "request": "^2.88.0", "retry": "^0.12.0", - "rimraf": "^2.6.3", + "rimraf": "^2.7.1", "safe-buffer": "^5.1.2", "semver": "^5.7.1", "sha": "^3.0.0", @@ -12910,7 +12278,7 @@ } }, "chownr": { - "version": "1.1.3", + "version": "1.1.4", "bundled": true }, "ci-info": { @@ -13175,7 +12543,7 @@ "bundled": true }, "deep-extend": { - "version": "0.5.1", + "version": "0.6.0", "bundled": true }, "defaults": { @@ -13612,7 +12980,7 @@ } }, "glob": { - "version": "7.1.4", + "version": "7.1.6", "bundled": true, "requires": { "fs.realpath": "^1.0.0", @@ -13654,7 +13022,7 @@ } }, "graceful-fs": { - "version": "4.2.3", + "version": "4.2.4", "bundled": true }, "har-schema": { @@ -13689,7 +13057,7 @@ "bundled": true }, "hosted-git-info": { - "version": "2.8.5", + "version": "2.8.8", "bundled": true }, "http-cache-semantics": { @@ -13805,10 +13173,10 @@ "bundled": true }, "is-ci": { - "version": "1.1.0", + "version": "1.2.1", "bundled": true, "requires": { - "ci-info": "^1.0.0" + "ci-info": "^1.5.0" }, "dependencies": { "ci-info": { @@ -13870,7 +13238,7 @@ } }, "is-retry-allowed": { - "version": "1.1.0", + "version": "1.2.0", "bundled": true }, "is-stream": { @@ -14283,10 +13651,6 @@ "brace-expansion": "^1.1.7" } }, - "minimist": { - "version": "0.0.8", - "bundled": true - }, "minizlib": { "version": "1.3.3", "bundled": true, @@ -14321,10 +13685,16 @@ } }, "mkdirp": { - "version": "0.5.1", + "version": "0.5.5", "bundled": true, "requires": { - "minimist": "0.0.8" + "minimist": "^1.2.5" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "bundled": true + } } }, "move-concurrently": { @@ -14367,7 +13737,7 @@ } }, "node-gyp": { - "version": "5.0.7", + "version": "5.1.0", "bundled": true, "requires": { "env-paths": "^2.2.0", @@ -14384,7 +13754,7 @@ } }, "nopt": { - "version": "4.0.1", + "version": "4.0.3", "bundled": true, "requires": { "abbrev": "1", @@ -14469,11 +13839,12 @@ } }, "npm-packlist": { - "version": "1.4.7", + "version": "1.4.8", "bundled": true, "requires": { "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" } }, "npm-pick-manifest": { @@ -14486,7 +13857,7 @@ } }, "npm-profile": { - "version": "4.0.2", + "version": "4.0.4", "bundled": true, "requires": { "aproba": "^1.1.2 || 2", @@ -14495,7 +13866,7 @@ } }, "npm-registry-fetch": { - "version": "4.0.2", + "version": "4.0.4", "bundled": true, "requires": { "JSONStream": "^1.3.4", @@ -14874,17 +14245,17 @@ "bundled": true }, "rc": { - "version": "1.2.7", + "version": "1.2.8", "bundled": true, "requires": { - "deep-extend": "^0.5.1", + "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "dependencies": { "minimist": { - "version": "1.2.0", + "version": "1.2.5", "bundled": true } } @@ -14937,7 +14308,7 @@ } }, "readable-stream": { - "version": "3.4.0", + "version": "3.6.0", "bundled": true, "requires": { "inherits": "^2.0.3", @@ -14956,7 +14327,7 @@ } }, "registry-auth-token": { - "version": "3.3.2", + "version": "3.4.0", "bundled": true, "requires": { "rc": "^1.1.6", @@ -15013,7 +14384,7 @@ "bundled": true }, "rimraf": { - "version": "2.6.3", + "version": "2.7.1", "bundled": true, "requires": { "glob": "^7.1.3" @@ -15272,10 +14643,16 @@ } }, "string_decoder": { - "version": "1.2.0", + "version": "1.3.0", "bundled": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.0", + "bundled": true + } } }, "stringify-package": { @@ -15544,7 +14921,7 @@ } }, "widest-line": { - "version": "2.0.0", + "version": "2.0.1", "bundled": true, "requires": { "string-width": "^2.1.1" @@ -16176,18 +15553,18 @@ "dev": true }, "pako": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", - "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "dev": true }, "parallel-transform": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", - "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", "dev": true, "requires": { - "cyclist": "~0.2.2", + "cyclist": "^1.0.1", "inherits": "^2.0.3", "readable-stream": "^2.1.5" } @@ -16325,20 +15702,18 @@ "dev": true }, "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "pify": "^3.0.0" }, "dependencies": { "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true } } @@ -16404,6 +15779,60 @@ "find-up": "^3.0.0" } }, + "pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", + "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + } + } + }, "please-upgrade-node": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.1.1.tgz", @@ -17479,6 +16908,14 @@ "parse-asn1": "^5.0.0", "randombytes": "^2.0.1", "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + } } }, "pullstream": { @@ -17668,43 +17105,67 @@ } }, "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", "dev": true, "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" }, "dependencies": { "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" + "locate-path": "^2.0.0" } }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, "requires": { - "pinkie-promise": "^2.0.0" + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" } }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", "dev": true, "requires": { - "load-json-file": "^1.0.0", + "load-json-file": "^4.0.0", "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" + "path-type": "^3.0.0" } } } @@ -17745,24 +17206,13 @@ } }, "redent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", + "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", "dev": true, "requires": { - "indent-string": "^2.1.0", - "strip-indent": "^1.0.1" - }, - "dependencies": { - "indent-string": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", - "dev": true, - "requires": { - "repeating": "^2.0.0" - } - } + "indent-string": "^3.0.0", + "strip-indent": "^2.0.0" } }, "regenerate": { @@ -17772,9 +17222,9 @@ "dev": true }, "regenerate-unicode-properties": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz", - "integrity": "sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", + "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", "dev": true, "requires": { "regenerate": "^1.4.0" @@ -17787,12 +17237,13 @@ "dev": true }, "regenerator-transform": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.1.tgz", - "integrity": "sha512-flVuee02C3FKRISbxhXl9mGzdbWUVHubl1SMaknjxkFB1/iqpJhArQUvRxOOPEc/9tAiX0BaQ28FJH10E4isSQ==", + "version": "0.14.4", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.4.tgz", + "integrity": "sha512-EaJaKPBI9GvKpvUz2mz4fhx7WPgvwRLY9v3hlNHWmAuJHI13T4nwKnNvm5RWJzEdnI5g5UwtOww+S8IdoUC2bw==", "dev": true, "requires": { - "private": "^0.1.6" + "@babel/runtime": "^7.8.4", + "private": "^0.1.8" } }, "regex-not": { @@ -17818,17 +17269,17 @@ "dev": true }, "regexpu-core": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.6.0.tgz", - "integrity": "sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz", + "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==", "dev": true, "requires": { "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.1.0", - "regjsgen": "^0.5.0", - "regjsparser": "^0.6.0", + "regenerate-unicode-properties": "^8.2.0", + "regjsgen": "^0.5.1", + "regjsparser": "^0.6.4", "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.1.0" + "unicode-match-property-value-ecmascript": "^1.2.0" } }, "regjsgen": { @@ -17838,9 +17289,9 @@ "dev": true }, "regjsparser": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz", - "integrity": "sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", + "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", "dev": true, "requires": { "jsesc": "~0.5.0" @@ -17861,10 +17312,36 @@ "dev": true }, "remove-files-webpack-plugin": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/remove-files-webpack-plugin/-/remove-files-webpack-plugin-1.1.3.tgz", - "integrity": "sha512-r53wQ/IlTkmcv11wri71CZ27S+GhFI5SjHbTbaAJbisPC3qGwg87vlA2C5Z1PuVA+aMI8SgimnE4SqI+ZYzu6Q==", - "dev": true + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/remove-files-webpack-plugin/-/remove-files-webpack-plugin-1.4.1.tgz", + "integrity": "sha512-BlINlu1rnLEPK1+W+686mlJ5G0NqUQOeFVGMmZGwRyVw2vxYH95mSHa92qahTOjYOdfe3BkthWdoq4a26PCXuA==", + "dev": true, + "requires": { + "@types/webpack": "4.41.12", + "trash": "6.1.1" + }, + "dependencies": { + "@types/webpack": { + "version": "4.41.12", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.12.tgz", + "integrity": "sha512-BpCtM4NnBen6W+KEhrL9jKuZCXVtiH6+0b6cxdvNt2EwU949Al334PjQSl2BeAyvAX9mgoNNG21wvjP3xZJJ5w==", + "dev": true, + "requires": { + "@types/anymatch": "*", + "@types/node": "*", + "@types/tapable": "*", + "@types/uglify-js": "*", + "@types/webpack-sources": "*", + "source-map": "^0.6.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } }, "remove-trailing-separator": { "version": "1.1.0", @@ -17937,9 +17414,9 @@ } }, "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", "dev": true, "requires": { "aws-sign2": "~0.7.0", @@ -17949,7 +17426,7 @@ "extend": "~3.0.2", "forever-agent": "~0.6.1", "form-data": "~2.3.2", - "har-validator": "~5.1.0", + "har-validator": "~5.1.3", "http-signature": "~1.2.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", @@ -17959,7 +17436,7 @@ "performance-now": "^2.1.0", "qs": "~6.5.2", "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", + "tough-cookie": "~2.5.0", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" }, @@ -17969,6 +17446,16 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", "dev": true + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } } } }, @@ -18183,9 +17670,9 @@ "dev": true }, "rfg-api": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/rfg-api/-/rfg-api-0.5.0.tgz", - "integrity": "sha512-wd6BcVoBsEHlbfsaB1WD4Z/Xis4uEP+Qctd3u2jxXR5yTkCYENaB/m3Jsk0G2qToKgAeE+6tbsN6T0n8DHcSaw==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/rfg-api/-/rfg-api-0.5.1.tgz", + "integrity": "sha512-F4JJDx7gTFcGNybw+0trDFSYjuy2hbdqDqdaNBkMdSVlasglkfwyTWHTFqSUOLszbpZLwatLy0dxAJonNZi6hw==", "dev": true, "requires": { "axios": "^0.18.0", @@ -18193,6 +17680,23 @@ "metaparser": "^1.0.7", "mkdirp": "^0.5.0", "node-unzip-2": "^0.2.7" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + } } }, "rgb-regex": { @@ -18281,9 +17785,9 @@ "dev": true }, "sass": { - "version": "1.23.7", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.23.7.tgz", - "integrity": "sha512-cYgc0fanwIpi0rXisGxl+/wadVQ/HX3RhpdRcjLdj2o2ye/sxUTpAxIhbmJy3PLQgRFbf6Pn8Jsrta2vdXcoOQ==", + "version": "1.26.5", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.26.5.tgz", + "integrity": "sha512-FG2swzaZUiX53YzZSjSakzvGtlds0lcbF+URuU9mxOv7WBh7NhXEVDa4kPKN4hN6fC2TkOTOKqiqp6d53N9X5Q==", "dev": true, "requires": { "chokidar": ">=2.0.0 <4.0.0" @@ -18392,6 +17896,12 @@ } } }, + "serialize-javascript": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", + "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", + "dev": true + }, "serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", @@ -18847,9 +18357,9 @@ } }, "sodium-javascript": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/sodium-javascript/-/sodium-javascript-0.5.5.tgz", - "integrity": "sha512-UMmCHovws/sxIBZsIRhIl8uRPou/RFDD0vVop81T1hG106NLLgqajKKuHAOtAP6hflnZ0UrVA2VFwddTd/NQyA==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/sodium-javascript/-/sodium-javascript-0.5.6.tgz", + "integrity": "sha512-Uk+JpqHEbzsEmiMxwL7TB/ndhMEpc52KdReYXXSIX2oRFPaI7ZDlDImF8KbkFWbYl9BJRtc82AZ/kNf4/0n9KA==", "requires": { "blake2b": "^2.1.1", "nanoassert": "^1.0.0", @@ -19235,9 +18745,9 @@ } }, "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -19313,9 +18823,9 @@ } }, "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", "dev": true }, "streamroller": { @@ -19465,13 +18975,10 @@ } }, "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "^0.2.0" - } + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true }, "strip-eof": { "version": "1.0.0", @@ -19480,21 +18987,10 @@ "dev": true }, "strip-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", - "dev": true, - "requires": { - "get-stdin": "^4.0.1" - }, - "dependencies": { - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true - } - } + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", + "dev": true }, "strip-json-comments": { "version": "2.0.1", @@ -19573,6 +19069,23 @@ "stable": "^0.1.8", "unquote": "~1.1.1", "util.promisify": "~1.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + } } }, "symbol-observable": { @@ -19638,9 +19151,9 @@ "dev": true }, "terser": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.4.2.tgz", - "integrity": "sha512-Uufrsvhj9O1ikwgITGsZ5EZS6qPokUOkCegS7fYOdGTv+OA90vndUbU6PEjr5ePqHfNUbGyMO7xyIZv2MhsALQ==", + "version": "4.6.13", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.13.tgz", + "integrity": "sha512-wMvqukYgVpQlymbnNbabVZbtM6PN63AzqexpwJL8tbh/mRT9LE5o+ruVduAGL7D6Fpjl+Q+06U5I9Ul82odAhw==", "dev": true, "requires": { "commander": "^2.20.0", @@ -19655,9 +19168,9 @@ "dev": true }, "source-map-support": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", - "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -19667,28 +19180,29 @@ } }, "terser-webpack-plugin": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.2.3.tgz", - "integrity": "sha512-R8cqQDld4BjVKaIR0D9Q0O/QzfgXzME3wXfSxxW23ZGv5xpMDUt1NEnCuG94y1+bKASLvc5TjIHLlNo0eK8GLA==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.6.tgz", + "integrity": "sha512-I8IDsQwZrqjdmOicNeE8L/MhwatAap3mUrtcAKJuilsemUNcX+Hier/eAzwStVqhlCxq0aG3ni9bK/0BESXkTg==", "dev": true, "requires": { "cacache": "^13.0.1", - "find-cache-dir": "^3.1.0", - "jest-worker": "^24.9.0", - "schema-utils": "^2.6.1", - "serialize-javascript": "^2.1.2", + "find-cache-dir": "^3.3.1", + "jest-worker": "^25.4.0", + "p-limit": "^2.3.0", + "schema-utils": "^2.6.6", + "serialize-javascript": "^3.0.0", "source-map": "^0.6.1", - "terser": "^4.4.2", + "terser": "^4.6.12", "webpack-sources": "^1.4.3" }, "dependencies": { "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", + "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", "dev": true, "requires": { - "fast-deep-equal": "^2.0.1", + "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" @@ -19700,14 +19214,46 @@ "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", "dev": true }, + "cacache": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-13.0.1.tgz", + "integrity": "sha512-5ZvAxd05HDDU+y9BVvcqYu2LLXmPnQ0hW62h32g4xBTgL/MppR4/04NHfj/ycM2y6lmTnbw6HVi+1eN0Psba6w==", + "dev": true, + "requires": { + "chownr": "^1.1.2", + "figgy-pudding": "^3.5.1", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.2", + "infer-owner": "^1.0.4", + "lru-cache": "^5.1.1", + "minipass": "^3.0.0", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "p-map": "^3.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^2.7.1", + "ssri": "^7.0.0", + "unique-filename": "^1.1.1" + } + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "dev": true + }, "find-cache-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.1.0.tgz", - "integrity": "sha512-zw+EFiNBNPgI2NTrKkDd1xd7q0cs6wr/iWnr/oUkI0yF9K9GqQ+riIt4aiyFaaqpaWbxPrJXHI+QvmNUQbX+0Q==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", "dev": true, "requires": { "commondir": "^1.0.1", - "make-dir": "^3.0.0", + "make-dir": "^3.0.2", "pkg-dir": "^4.1.0" } }, @@ -19721,6 +19267,12 @@ "path-exists": "^4.0.0" } }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -19730,15 +19282,48 @@ "p-locate": "^4.1.0" } }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, "make-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.0.tgz", - "integrity": "sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, "requires": { "semver": "^6.0.0" } }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, "p-locate": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", @@ -19748,6 +19333,15 @@ "p-limit": "^2.2.0" } }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -19763,26 +19357,29 @@ "find-up": "^4.0.0" } }, - "schema-utils": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.1.tgz", - "integrity": "sha512-0WXHDs1VDJyo+Zqs9TKLKyD/h7yDpHUhEFsM2CzkICFdoX1av+GBq/J2xRTFfsQO5kBfhZzANf2VcIm84jqDbg==", + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dev": true, "requires": { - "ajv": "^6.10.2", + "glob": "^7.1.3" + } + }, + "schema-utils": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.6.tgz", + "integrity": "sha512-wHutF/WPSbIi9x6ctjGGk2Hvl0VOz5l3EKEuKbjPlB30mKZUzb9A5k9yEXRX3pwyqVLPvpfZZEllaFq/M718hA==", + "dev": true, + "requires": { + "ajv": "^6.12.0", "ajv-keywords": "^3.4.1" } }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, "serialize-javascript": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", - "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.0.0.tgz", + "integrity": "sha512-skZcHYw2vEX4bw90nAr2iTTsz6x2SrHEnfxgKYmZlvJYBEZrvbKtobJWlQ20zczKb3bsHHXXTYt48zBA7ni9cw==", "dev": true }, "source-map": { @@ -19791,6 +19388,16 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, + "ssri": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-7.1.0.tgz", + "integrity": "sha512-77/WrDZUWocK0mvA5NTRQyveUf+wsrIc6vyrxpS8tVvYBcX215QbafrJR3KtkpskIzoFLqqNuuYQvxaMjXJ/0g==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1", + "minipass": "^3.1.1" + } + }, "webpack-sources": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", @@ -19800,6 +19407,12 @@ "source-list-map": "^2.0.0", "source-map": "~0.6.1" } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true } } }, @@ -19961,6 +19574,81 @@ } } }, + "trash": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/trash/-/trash-6.1.1.tgz", + "integrity": "sha512-4i56lCmz2RG6WZN018hf4L75L5HboaFuKkHx3wDG/ihevI99e0OgFyl8w6G4ioqBm62V4EJqCy5xw3vQSNXU8A==", + "dev": true, + "requires": { + "@stroncium/procfs": "^1.0.0", + "globby": "^7.1.1", + "is-path-inside": "^3.0.2", + "make-dir": "^3.0.0", + "move-file": "^1.1.0", + "p-map": "^3.0.0", + "p-try": "^2.2.0", + "uuid": "^3.3.2", + "xdg-trashdir": "^2.1.1" + }, + "dependencies": { + "globby": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", + "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "dir-glob": "^2.0.0", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + } + }, + "ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", + "dev": true + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + } + } + }, "traverse": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", @@ -19968,9 +19656,9 @@ "dev": true }, "trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", + "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", "dev": true }, "trim-off-newlines": { @@ -20056,20 +19744,19 @@ "dev": true }, "uglify-js": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", - "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.9.2.tgz", + "integrity": "sha512-zGVwKslUAD/EeqOrD1nQaBmXIHl1Vw371we8cvS8I6mYK9rmgX5tv8AAeJdfsQ3Kk5mGax2SVV/AizxdNGhl7Q==", "dev": true, "optional": true, "requires": { - "commander": "~2.20.0", - "source-map": "~0.6.1" + "commander": "~2.20.3" }, "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true, "optional": true } @@ -20082,9 +19769,9 @@ "dev": true }, "underscore": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.2.tgz", - "integrity": "sha512-D39qtimx0c1fI3ya1Lnhk3E9nONswSKhnffBI0gME9C99fYOkNi04xs8K6pePLhvl1frbDemkaBQ5ikWllR2HQ==", + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz", + "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==", "dev": true }, "unicode-canonical-property-names-ecmascript": { @@ -20104,15 +19791,15 @@ } }, "unicode-match-property-value-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz", - "integrity": "sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", + "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", "dev": true }, "unicode-property-aliases-ecmascript": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz", - "integrity": "sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", + "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", "dev": true }, "union-value": { @@ -20272,6 +19959,15 @@ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, + "user-home": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", + "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", + "dev": true, + "requires": { + "os-homedir": "^1.0.0" + } + }, "useragent": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz", @@ -20388,27 +20084,27 @@ "dev": true }, "watchpack": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", - "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.1.tgz", + "integrity": "sha512-+IF9hfUFOrYOOaKyfaI7h7dquUIOgyEMoQMLA7OP5FxegKA2+XdXThAZ9TU2kucfhDH7rfMHs1oPYziVGWRnZA==", "dev": true, "requires": { - "chokidar": "^2.0.2", + "chokidar": "^2.1.8", "graceful-fs": "^4.1.2", "neo-async": "^2.5.0" } }, "webpack": { - "version": "4.41.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.2.tgz", - "integrity": "sha512-Zhw69edTGfbz9/8JJoyRQ/pq8FYUoY0diOXqW0T6yhgdhCv6wr0hra5DwwWexNRns2Z2+gsnrNcbe9hbGBgk/A==", + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.43.0.tgz", + "integrity": "sha512-GW1LjnPipFW2Y78OOab8NJlCflB7EFskMih2AHdvjbpKMeDJqEgSx24cXXXiPS65+WSwVyxtDsJH6jGX2czy+g==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-module-context": "1.8.5", - "@webassemblyjs/wasm-edit": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5", - "acorn": "^6.2.1", + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/wasm-edit": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "acorn": "^6.4.1", "ajv": "^6.10.2", "ajv-keywords": "^3.4.1", "chrome-trace-event": "^1.0.2", @@ -20419,29 +20115,29 @@ "loader-utils": "^1.2.3", "memory-fs": "^0.4.1", "micromatch": "^3.1.10", - "mkdirp": "^0.5.1", + "mkdirp": "^0.5.3", "neo-async": "^2.6.1", "node-libs-browser": "^2.2.1", "schema-utils": "^1.0.0", "tapable": "^1.1.3", - "terser-webpack-plugin": "^1.4.1", - "watchpack": "^1.6.0", + "terser-webpack-plugin": "^1.4.3", + "watchpack": "^1.6.1", "webpack-sources": "^1.4.1" }, "dependencies": { "acorn": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz", - "integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", "dev": true }, "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", + "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", "dev": true, "requires": { - "fast-deep-equal": "^2.0.1", + "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" @@ -20453,44 +20149,27 @@ "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", "dev": true }, - "cacache": { - "version": "12.0.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", - "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", - "dev": true, - "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - } - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "serialize-javascript": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", - "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", "dev": true }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -20523,25 +20202,13 @@ "source-list-map": "^2.0.0", "source-map": "~0.6.1" } - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true } } }, "webpack-cli": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.10.tgz", - "integrity": "sha512-u1dgND9+MXaEt74sJR4PR7qkPxXUSQ0RXYq8x1L6Jg1MYVEmGPrH6Ah6C4arD4r0J1P5HKjRqpab36k0eIzPqg==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.11.tgz", + "integrity": "sha512-dXlfuml7xvAFwYUPsrtQAA9e4DOe58gnzSxhgrO/ZM/gyXTBowrsYeubyN4mqGhYdpXMFNyQ6emjJS9M7OBd4g==", "dev": true, "requires": { "chalk": "2.4.2", @@ -20652,12 +20319,6 @@ "has-flag": "^3.0.0" } }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, "wrap-ansi": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", @@ -20669,12 +20330,6 @@ "strip-ansi": "^5.0.0" } }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, "yargs": { "version": "13.2.4", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz", @@ -20695,9 +20350,9 @@ } }, "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -20860,6 +20515,23 @@ "dev": true, "requires": { "mkdirp": "^0.5.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + } } }, "ws": { @@ -20873,6 +20545,36 @@ "ultron": "~1.1.0" } }, + "xdg-basedir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-2.0.0.tgz", + "integrity": "sha1-7byQPMOF/ARSPZZqM1UEtVBNG9I=", + "dev": true, + "requires": { + "os-homedir": "^1.0.0" + } + }, + "xdg-trashdir": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/xdg-trashdir/-/xdg-trashdir-2.1.1.tgz", + "integrity": "sha512-KcVhPaOu2ZurYNHSRTf1+ZHORkTZGCQ+u0JHN17QixRISJq4pXOnjt/lQcehvtHL5QAKhSzKgyjrcNnPdkPBHA==", + "dev": true, + "requires": { + "@sindresorhus/df": "^2.1.0", + "mount-point": "^3.0.0", + "pify": "^2.2.0", + "user-home": "^2.0.0", + "xdg-basedir": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, "xmlhttprequest-ssl": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", diff --git a/package.json b/package.json index 2e78c20e9..de74e0d46 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "uni2work", - "version": "13.0.1", + "version": "16.0.5", "description": "", "keywords": [], "author": "", @@ -53,28 +53,28 @@ "defaults" ], "devDependencies": { - "@babel/cli": "^7.7.5", - "@babel/core": "^7.7.5", - "@babel/plugin-proposal-class-properties": "^7.7.4", - "@babel/plugin-proposal-decorators": "^7.7.4", - "@babel/plugin-transform-runtime": "^7.7.6", - "@babel/preset-env": "^7.7.6", - "@commitlint/cli": "^8.2.0", - "@commitlint/config-conventional": "^8.2.0", - "@fortawesome/fontawesome-pro": "^5.12.0", - "autoprefixer": "^9.7.3", + "@babel/cli": "^7.8.4", + "@babel/core": "^7.9.6", + "@babel/plugin-proposal-class-properties": "^7.8.3", + "@babel/plugin-proposal-decorators": "^7.8.3", + "@babel/plugin-transform-runtime": "^7.9.6", + "@babel/preset-env": "^7.9.6", + "@commitlint/cli": "^8.3.5", + "@commitlint/config-conventional": "^8.3.4", + "@fortawesome/fontawesome-pro": "^5.13.0", + "autoprefixer": "^9.7.6", "babel-core": "^6.26.3", - "babel-eslint": "^10.0.3", - "babel-loader": "^8.0.6", + "babel-eslint": "^10.1.0", + "babel-loader": "^8.1.0", "babel-plugin-syntax-dynamic-import": "^6.18.0", "babel-plugin-transform-decorators-legacy": "^1.3.5", "babel-preset-es2015": "^6.24.1", "cbt_tunnels": "^1.2.2", "clean-webpack-plugin": "^3.0.0", - "copy-webpack-plugin": "^5.1.0", + "copy-webpack-plugin": "^5.1.1", "css-loader": "^2.1.1", "eslint": "^5.16.0", - "file-loader": "^5.0.2", + "file-loader": "^5.1.0", "fs-extra": "^8.1.0", "glob": "^7.1.6", "html-webpack-plugin": "^3.2.0", @@ -85,45 +85,45 @@ "karma-chrome-launcher": "^2.2.0", "karma-cli": "^2.0.0", "karma-jasmine": "^2.0.1", - "karma-jasmine-html-reporter": "^1.4.2", + "karma-jasmine-html-reporter": "^1.5.3", "karma-mocha-reporter": "^2.2.5", "karma-webpack": "^3.0.5", "lint-staged": "^8.2.1", "lodash.debounce": "^4.0.8", - "mini-css-extract-plugin": "^0.8.0", + "mini-css-extract-plugin": "^0.8.2", "npm-run-all": "^4.1.5", "null-loader": "^2.0.0", "optimize-css-assets-webpack-plugin": "^5.0.3", "postcss-loader": "^3.0.0", "postcss-preset-env": "^6.7.0", "real-favicon-webpack-plugin": "^0.2.3", - "remove-files-webpack-plugin": "^1.1.3", - "request": "^2.88.0", + "remove-files-webpack-plugin": "^1.4.1", + "request": "^2.88.2", "request-promise": "^4.2.5", "resolve-url-loader": "^3.1.1", - "sass": "^1.23.7", + "sass": "^1.26.5", "sass-loader": "^7.3.1", "semver": "^6.3.0", "standard-version": "^6.0.1", "style-loader": "^0.23.1", - "terser-webpack-plugin": "^2.2.3", + "terser-webpack-plugin": "^2.3.6", "tmp": "^0.1.0", "typeface-roboto": "0.0.75", "typeface-source-sans-pro": "0.0.75", - "webpack": "^4.41.2", - "webpack-cli": "^3.3.10", + "webpack": "^4.43.0", + "webpack-cli": "^3.3.11", "webpack-manifest-plugin": "^2.2.0", "webpack-plugin-hash-output": "^3.2.1" }, "dependencies": { - "@babel/runtime": "^7.7.6", + "@babel/runtime": "^7.9.6", "@juggle/resize-observer": "^2.5.0", - "core-js": "^3.4.8", + "core-js": "^3.6.5", "js-cookie": "^2.2.1", "lodash.throttle": "^4.1.1", - "moment": "^2.24.0", - "npm": "^6.13.7", - "sodium-javascript": "^0.5.5", + "moment": "^2.25.3", + "npm": "^6.14.5", + "sodium-javascript": "^0.5.6", "tail.datetime": "git+ssh://git@gitlab2.rz.ifi.lmu.de/uni2work/tail.DateTime.git#master", "whatwg-fetch": "^3.0.0" } diff --git a/package.yaml b/package.yaml index e5af6ee7f..9da8ce23c 100644 --- a/package.yaml +++ b/package.yaml @@ -1,5 +1,5 @@ name: uniworx -version: 13.0.1 +version: 16.0.5 dependencies: - base @@ -61,6 +61,7 @@ dependencies: - cryptoids - cryptoids-class - binary + - binary-instances - cereal - mtl - esqueleto >=3.1.0 @@ -103,7 +104,9 @@ dependencies: - postgresql-simple - word24 - mmorph - - clientsession + - serversession + - serversession-backend-acid-state + - acid-state - monad-memo - xss-sanitize - text-metrics @@ -138,7 +141,12 @@ dependencies: - wai-middleware-prometheus - extended-reals - rfc5051 + - unidecode - pandoc + - token-bucket + - async + - pointedlist + - clock other-extensions: - GeneralizedNewtypeDeriving @@ -185,8 +193,10 @@ default-extensions: - DeriveFunctor - DeriveFoldable - DeriveTraversable + - DeriveAnyClass - DerivingStrategies - DerivingVia + - GeneralizedNewtypeDeriving - DataKinds - BinaryLiterals - PolyKinds @@ -227,6 +237,8 @@ library: - -ddump-splices - -ddump-to-file cpp-options: -DDEVELOPMENT + ghc-prof-options: + - -fprof-auto else: ghc-options: - -O2 @@ -249,7 +261,8 @@ executables: source-dirs: test dependencies: - uniworx - other-modules: [] + other-modules: + - Database.Fill when: - condition: flag(library-only) buildable: false diff --git a/records.json b/records.json index e9a1244c8..1df7d5606 100644 --- a/records.json +++ b/records.json @@ -815,18 +815,5 @@ "usedIds": [] } } - ], - "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/inputs/radiobox.sass": [ - { - "modules": { - "byIdentifier": {}, - "usedIds": {} - }, - "chunks": { - "byName": {}, - "bySource": {}, - "usedIds": [] - } - } ] } \ No newline at end of file diff --git a/routes b/routes index 0e6777f0a..374d94282 100644 --- a/routes +++ b/routes @@ -55,6 +55,7 @@ /admin/features AdminFeaturesR GET POST /admin/test AdminTestR GET POST /admin/errMsg AdminErrMsgR GET POST +/admin/tokens AdminTokensR GET POST /health HealthR GET !free /instance InstanceR GET !free @@ -63,6 +64,7 @@ /info/legal LegalR GET !free /info/allocation InfoAllocationR GET !free /info/glossary GlossaryR GET !free +/info/faq FaqR GET !free /version VersionR GET !free /help HelpR GET POST !free @@ -110,6 +112,8 @@ /course/#CryptoUUIDCourse/apply AApplyR POST !timeANDallocation-registered /users AUsersR GET POST !allocation-admin /priorities APriosR GET POST !allocation-admin + /compute AComputeR GET POST !allocation-admin + /accept AAcceptR GET POST !allocation-admin /participants ParticipantsListR GET !evaluation /participants/#TermId/#SchoolId ParticipantsR GET !evaluation @@ -137,9 +141,9 @@ /exam-office CExamOfficeR GET POST !course-registered /subs CCorrectionsR GET POST /subs/assigned CAssignR GET POST - /sheet SheetListR GET !course-registered !materials !corrector + /sheet SheetListR GET !course-registered !materials !corrector !tutor /sheet/new SheetNewR GET POST - /sheet/current SheetCurrentR GET !course-registered !materials !corrector + /sheet/current SheetCurrentR GET !course-registered !materials !corrector !tutor /sheet/unassigned SheetOldUnassignedR GET /sheet/#SheetName SheetR: /show SShowR GET !timeANDcourse-registered !timeANDmaterials !corrector !timeANDtutor @@ -147,15 +151,15 @@ /edit SEditR GET POST /delete SDelR GET POST /subs SSubsR GET POST -- for lecturer only - !/subs/new SubmissionNewR GET POST !timeANDcourse-registeredANDuser-submissions + !/subs/new SubmissionNewR GET POST !timeANDcourse-registeredANDuser-submissionsANDsubmission-group !/subs/own SubmissionOwnR GET !free -- just redirect !/subs/assign SAssignR GET POST !lecturerANDtime /subs/#CryptoFileNameSubmission SubmissionR: - / SubShowR GET POST !ownerANDtimeANDuser-submissions !ownerANDread !correctorANDread + / SubShowR GET POST !ownerANDtimeANDuser-submissionsANDsubmission-group !ownerANDread !correctorANDread /delete SubDelR GET POST !ownerANDtimeANDuser-submissions /assign SubAssignR GET POST !lecturerANDtime /correction CorrectionR GET POST !corrector !ownerANDreadANDrated - /invite SInviteR GET POST !ownerANDtimeANDuser-submissions + /invite SInviteR GET POST !ownerANDtimeANDuser-submissionsANDsubmission-group !/#SubmissionFileType SubArchiveR GET !owner !corrector !/#SubmissionFileType/*FilePath SubDownloadR GET !owner !corrector /iscorrector SIsCorrR GET !corrector -- Route is used to check for corrector access to this sheet @@ -219,8 +223,9 @@ /subs/download CorrectionsDownloadR GET !corrector !lecturer -/msgs MessageListR GET POST -/msg/#{CryptoUUIDSystemMessage} MessageR GET POST !timeANDreadANDauthentication +/msgs MessageListR GET POST +/msg/#{CryptoUUIDSystemMessage} MessageR GET POST !timeANDreadANDauthentication +/msg/#{CryptoUUIDSystemMessage}/hide MessageHideR POST !timeANDauthentication !/#UUID CryptoUUIDDispatchR GET !free -- just redirect -- !/*{CI FilePath} CryptoFileNameDispatchR GET !free -- Disabled until preliminary check for valid cID exists diff --git a/shell.nix b/shell.nix index 9a3306edc..c76f4e99d 100644 --- a/shell.nix +++ b/shell.nix @@ -19,11 +19,22 @@ let ''; override = oldAttrs: { - nativeBuildInputs = oldAttrs.nativeBuildInputs ++ (with pkgs; [ nodejs-13_x postgresql openldap google-chrome exiftool ]) ++ (with pkgs.haskellPackages; [ stack yesod-bin hlint cabal-install weeder ]); + nativeBuildInputs = oldAttrs.nativeBuildInputs ++ (with pkgs; [ nodejs-13_x postgresql openldap google-chrome exiftool memcached ]) ++ (with pkgs.haskellPackages; [ stack yesod-bin hlint cabal-install weeder profiteur ]); shellHook = '' export PROMPT_INFO="${oldAttrs.name}" - export CHROME_BIN=$(which google-chrome-stable) + export EDITOR=emacsclient + + cleanup() { + set +e -x + type cleanup_postgres &>/dev/null && cleanup_postgres + type cleanup_widget_memcached &>/dev/null && cleanup_widget_memcached + type cleanup_session_memcached &>/dev/null && cleanup_session_memcached + type cleanup_cache_memcached &>/dev/null && cleanup_cache_memcached + set +x + } + + trap cleanup EXIT if [[ -z "$PGHOST" ]]; then set -xe @@ -37,14 +48,57 @@ let psql -f ${postgresSchema} postgres printf "Postgres logfile is %s\nPostgres socket directory is %s\n" ''${pgLogFile} ''${pgSockDir} - cleanup() { + cleanup_postgres() { set +e -x pg_ctl stop -D ''${pgDir} rm -rvf ''${pgDir} ''${pgSockDir} ''${pgLogFile} set +x } - trap cleanup EXIT + set +xe + fi + + if [[ -z "$WIDGET_MEMCACHED_HOST" ]]; then + set -xe + + memcached -l localhost -p 11211 &>/dev/null & + widget_memcached_pid=$? + + cleanup_widget_memcached() { + [[ -n "$widget_memcached_pid" ]] && kill $widget_memcached_pid + } + + export WIDGET_MEMCACHED_HOST=localhost WIDGET_MEMCACHED_PORT=11211 + + set +xe + fi + + if [[ -z "$SESSION_MEMCACHED_HOST" ]]; then + set -xe + + memcached -l localhost -p 11212 &>/dev/null & + session_memcached_pid=$? + + cleanup_session_memcached() { + [[ -n "$session_memcached_pid" ]] && kill $session_memcached_pid + } + + export SESSION_MEMCACHED_HOST=localhost SESSION_MEMCACHED_PORT=11212 + + set +xe + fi + + if [[ -z "MEMCACHED_HOST" ]]; then + set -xe + + memcached -l localhost -p 11213 &>/dev/null & + memcached_pid=$? + + cleanup_session_memcached() { + [[ -n "memcached_pid" ]] && kill memcached_pid + } + + export MEMCACHED_HOST=localhost MEMCACHED_PORT=11212 set +xe fi diff --git a/src/Application.hs b/src/Application.hs index 59007c31e..51bef9a21 100644 --- a/src/Application.hs +++ b/src/Application.hs @@ -18,10 +18,11 @@ module Application import Control.Monad.Logger (liftLoc, LoggingT(..), MonadLoggerIO(..)) import Database.Persist.Postgresql (createPostgresqlPool, pgConnStr, - pgPoolSize, runSqlPool) -import Import hiding (cancel) + pgPoolSize, runSqlPool, ConnectionPool) +import Import hiding (cancel, respond) import Language.Haskell.TH.Syntax (qLocation) import Network.Wai (Middleware) +import qualified Network.Wai as Wai import Network.Wai.Handler.Warp (Settings, defaultSettings, defaultShouldDisplayException, runSettings, runSettingsSocket, setHost, @@ -40,6 +41,9 @@ import Handler.Utils (runAppLoggingT) import Foreign.Store +import Web.Cookie +import Network.HTTP.Types.Header (hSetCookie) + import qualified Data.UUID as UUID import qualified Data.UUID.V4 as UUID @@ -88,6 +92,9 @@ import qualified Data.Set as Set import Handler.Utils.Routes (classifyHandler) +import qualified Data.Acid.Memory as Acid +import qualified Web.ServerSession.Backend.Acid as Acid + -- Import all relevant handler modules here. -- (HPack takes care to add new modules to our cabal file nowadays.) import Handler.News @@ -125,7 +132,7 @@ mkYesodDispatch "UniWorX" resourcesUniWorX -- performs initialization and returns a foundation datatype value. This is also -- the place to put your migrate statements to have automatic database -- migrations handled by Yesod. -makeFoundation :: (MonadResource m, MonadUnliftIO m) => AppSettings -> m UniWorX +makeFoundation :: (MonadResource m, MonadUnliftIO m, MonadThrow m) => AppSettings -> m UniWorX makeFoundation appSettings'@AppSettings{..} = do registerGHCMetrics @@ -168,7 +175,7 @@ makeFoundation appSettings'@AppSettings{..} = do -- logging function. To get out of this loop, we initially create a -- temporary foundation without a real connection pool, get a log function -- from there, and then create the real foundation. - let mkFoundation appConnPool appSmtpPool appLdapPool appCryptoIDKey appSessionKey appSecretBoxKey appWidgetMemcached appJSONWebKeySet appClusterID = UniWorX {..} + let mkFoundation appConnPool appSmtpPool appLdapPool appCryptoIDKey appSessionStore appSecretBoxKey appWidgetMemcached appJSONWebKeySet appClusterID appMemcached = UniWorX {..} -- The UniWorX {..} syntax is an example of record wild cards. For more -- information, see: -- https://ocharles.org.uk/blog/posts/2014-12-04-record-wildcards.html @@ -177,11 +184,12 @@ makeFoundation appSettings'@AppSettings{..} = do (error "smtpPool forced in tempFoundation") (error "ldapPool forced in tempFoundation") (error "cryptoIDKey forced in tempFoundation") - (error "sessionKey forced in tempFoundation") + (error "sessionStore forced in tempFoundation") (error "secretBoxKey forced in tempFoundation") (error "widgetMemcached forced in tempFoundation") (error "JSONWebKeySet forced in tempFoundation") (error "ClusterID forced in tempFoundation") + (error "memcached forced in tempFoundation") runAppLoggingT tempFoundation $ do $logInfoS "InstanceID" $ UUID.toText appInstanceID @@ -191,9 +199,9 @@ makeFoundation appSettings'@AppSettings{..} = do $logDebugS "setup" "SMTP-Pool" createSmtpPool c - appWidgetMemcached <- for appWidgetMemcachedConf $ \c -> do + appWidgetMemcached <- for appWidgetMemcachedConf $ \WidgetMemcachedConf{ widgetMemcachedConf } -> do $logDebugS "setup" "Widget-Memcached" - createWidgetMemcached c + createMemcached widgetMemcachedConf -- Create the database connection pool $logDebugS "setup" "PostgreSQL-Pool" @@ -201,9 +209,9 @@ makeFoundation appSettings'@AppSettings{..} = do (pgConnStr appDatabaseConf) (pgPoolSize appDatabaseConf) - ldapPool <- for appLdapConf $ \LdapConf{..} -> do - $logDebugS "setup" "LDAP-Pool" - createLdapPool ldapHost ldapPort (poolStripes ldapPool) (poolTimeout ldapPool) ldapTimeout (poolLimit ldapPool) + ldapPool <- traverse mkFailover <=< forOf (traverse . traverse) appLdapConf $ \conf@LdapConf{..} -> do + $logDebugS "setup" $ "LDAP-Pool " <> tshow ldapHost + (conf,) <$> createLdapPool ldapHost ldapPort (poolStripes ldapPool) (poolTimeout ldapPool) ldapTimeout (poolLimit ldapPool) -- Perform database migration using our application's logging settings. if @@ -215,17 +223,48 @@ makeFoundation appSettings'@AppSettings{..} = do liftIO . exitWith $ ExitFailure 2 $logDebugS "setup" "Cluster-Config" appCryptoIDKey <- clusterSetting (Proxy :: Proxy 'ClusterCryptoIDKey) `runSqlPool` sqlPool - appSessionKey <- clusterSetting (Proxy :: Proxy 'ClusterClientSessionKey) `runSqlPool` sqlPool appSecretBoxKey <- clusterSetting (Proxy :: Proxy 'ClusterSecretBoxKey) `runSqlPool` sqlPool appJSONWebKeySet <- clusterSetting (Proxy :: Proxy 'ClusterJSONWebKeySet) `runSqlPool` sqlPool appClusterID <- clusterSetting (Proxy :: Proxy 'ClusterId) `runSqlPool` sqlPool - let foundation = mkFoundation sqlPool smtpPool ldapPool appCryptoIDKey appSessionKey appSecretBoxKey appWidgetMemcached appJSONWebKeySet appClusterID + appMemcached <- for appMemcachedConf $ \memcachedConf -> do + $logDebugS "setup" "Memcached" + memcachedKey <- clusterSetting (Proxy :: Proxy 'ClusterMemcachedKey) `runSqlPool` sqlPool + memcached <- createMemcached memcachedConf + return (memcachedKey, memcached) + + appSessionStore <- mkSessionStore appSettings' sqlPool `runSqlPool` sqlPool + + let foundation = mkFoundation sqlPool smtpPool ldapPool appCryptoIDKey appSessionStore appSecretBoxKey appWidgetMemcached appJSONWebKeySet appClusterID appMemcached -- Return the foundation $logDebugS "setup" "Done" return foundation +data SessionStoreException + = SessionStoreNotAvailable + deriving (Eq, Ord, Read, Show, Generic, Typeable) +instance Exception SessionStoreException + +mkSessionStore :: forall m. + ( MonadIO m + , MonadLogger m + , MonadThrow m + , MonadResource m + ) + => AppSettings -> ConnectionPool -> ReaderT SqlBackend m SomeSessionStorage +mkSessionStore AppSettings{..} mcdSqlConnPool + | Just mcdConf@MemcachedConf{..} <- appSessionMemcachedConf = do + mcdSqlMemcachedKey <- clusterSetting (Proxy :: Proxy 'ClusterServerSessionKey) + $logDebugS "setup" "Session-Memcached" + mcdSqlMemcached <- createMemcached mcdConf + let mcdSqlMemcachedExpiration = memcachedExpiry + return $ _SessionStorageMemcachedSql # MemcachedSqlStorage{..} + | appServerSessionAcidFallback = liftIO $ + review _SessionStorageAcid . Acid.AcidStorage <$> Acid.openMemoryState Acid.emptyState + | otherwise = throwM SessionStoreNotAvailable + + clusterSetting :: forall key m p. ( MonadIO m , ClusterSetting key @@ -285,8 +324,8 @@ createSmtpPool SmtpConf{ smtpPool = ResourcePoolConf{..}, .. } = do return conn liftIO $ createPool (mkConnection >>= maybe return applyAuth smtpAuth) reapConnection poolStripes poolTimeout poolLimit -createWidgetMemcached :: (MonadLogger m, MonadResource m) => WidgetMemcachedConf -> m Memcached.Connection -createWidgetMemcached WidgetMemcachedConf{widgetMemcachedConnectInfo} = snd <$> allocate (Memcached.connect widgetMemcachedConnectInfo) Memcached.close +createMemcached :: (MonadLogger m, MonadResource m) => MemcachedConf -> m Memcached.Connection +createMemcached MemcachedConf{memcachedConnectInfo} = snd <$> allocate (Memcached.connect memcachedConnectInfo) Memcached.close -- | Convert our foundation to a WAI Application by calling @toWaiAppPlain@ and -- applying some additional middlewares. @@ -295,7 +334,33 @@ makeApplication foundation = liftIO $ do logWare <- makeLogWare foundation -- Create the WAI application and apply middlewares appPlain <- toWaiAppPlain foundation - return . observeHTTPRequestLatency classifyHandler . logWare $ defaultMiddlewaresNoLogging appPlain + return . observeHTTPRequestLatency classifyHandler . logWare . normalizeCookies $ defaultMiddlewaresNoLogging appPlain + where + normalizeCookies :: Wai.Middleware + normalizeCookies app req respond = app req $ \res -> do + resHdrs' <- go $ Wai.responseHeaders res + respond $ Wai.mapResponseHeaders (const resHdrs') res + where parseSetCookie' :: ByteString -> IO (Maybe SetCookie) + parseSetCookie' = fmap (either (\(_ :: SomeException) -> Nothing) Just) . try . evaluate . force . parseSetCookie + + go [] = return [] + go (hdr@(hdrName, hdrValue) : hdrs) + | hdrName == hSetCookie = do + mcookieHdr <- parseSetCookie' hdrValue + case mcookieHdr of + Nothing -> (hdr :) <$> go hdrs + Just cookieHdr -> do + let cookieHdrMatches hdrValue' = maybeT (return False) $ do + cookieHdr' <- MaybeT $ parseSetCookie' hdrValue' + -- See https://tools.ietf.org/html/rfc6265 + guard $ setCookiePath cookieHdr' == setCookiePath cookieHdr + guard $ setCookieName cookieHdr' == setCookieName cookieHdr + guard $ setCookieDomain cookieHdr' == setCookieDomain cookieHdr + return True + others <- filterM (\(hdrName', hdrValue') -> and2M (pure $ hdrName' == hSetCookie) (cookieHdrMatches hdrValue')) hdrs + if | null others -> (hdr :) <$> go hdrs + | otherwise -> go hdrs + | otherwise = (hdr :) <$> go hdrs makeLogWare :: MonadIO m => UniWorX -> m Middleware makeLogWare app = do @@ -432,12 +497,12 @@ appMain = runResourceT $ do case watchdogMicroSec of Just wInterval | maybe True (== myProcessID) watchdogProcess - -> let notifyWatchdog :: forall a. IO a - notifyWatchdog = runAppLoggingT foundation $ go Nothing + -> let notifyWatchdog :: forall a m'. ( MonadLogger m', MonadIO m') => m' a + notifyWatchdog = go Nothing where - go :: Maybe (Set (UTCTime, HealthReport)) -> LoggingT IO a + go :: Maybe (Set (UTCTime, HealthReport)) -> m' a go pResults = do - let delay = floor $ wInterval % 2 + let delay = floor $ wInterval % 4 d <- liftIO $ newDelay delay $logDebugS "Notify" $ "Waiting up to " <> tshow delay <> "µs..." diff --git a/src/Audit/Types.hs b/src/Audit/Types.hs index 88add95c9..61c3628a9 100644 --- a/src/Audit/Types.hs +++ b/src/Audit/Types.hs @@ -178,6 +178,16 @@ data Transaction , transactionUser :: UserId } + | TransactionSubmissionGroupSet + { transactionCourse :: CourseId + , transactionUser :: UserId + , transactionSubmissionGroup :: SubmissionGroupName + } + | TransactionSubmissionGroupUnset + { transactionCourse :: CourseId + , transactionUser :: UserId + } + deriving (Eq, Ord, Read, Show, Generic, Typeable) deriveJSON defaultOptions diff --git a/src/Auth/LDAP.hs b/src/Auth/LDAP.hs index feaa31c44..8f0a40f98 100644 --- a/src/Auth/LDAP.hs +++ b/src/Auth/LDAP.hs @@ -3,6 +3,7 @@ module Auth.LDAP , campusLogin , CampusUserException(..) , campusUser, campusUser' + , campusUserReTest, campusUserReTest' , campusUserMatr, campusUserMatr' , CampusMessage(..) , ldapUserPrincipalName, ldapUserEmail, ldapUserDisplayName @@ -102,8 +103,18 @@ instance Exception CampusUserException makePrisms ''CampusUserException -campusUser :: MonadUnliftIO m => LdapConf -> LdapPool -> Creds site -> m (Ldap.AttrList []) -campusUser conf@LdapConf{..} pool Creds{..} = liftIO . (`catches` errHandlers) $ either (throwM . CampusUserLdapError) return <=< withLdap pool $ \ldap -> do +campusUserWith :: MonadUnliftIO m + => ( Lens (LdapConf, LdapPool) (LdapConf, Ldap) LdapPool Ldap + -> Failover (LdapConf, LdapPool) + -> FailoverMode + -> ((LdapConf, Ldap) -> IO (Ldap.AttrList [])) + -> IO (Either LdapPoolError (Ldap.AttrList [])) + ) + -> Failover (LdapConf, LdapPool) + -> FailoverMode + -> Creds site + -> m (Ldap.AttrList []) +campusUserWith withLdap' pool mode Creds{..} = liftIO . (`catches` errHandlers) $ either (throwM . CampusUserLdapError) return <=< withLdap' _2 pool mode $ \(conf@LdapConf{..}, ldap) -> do Ldap.bind ldap ldapDn ldapPassword results <- case lookup "DN" credsExtra of Just userDN -> do @@ -121,13 +132,23 @@ campusUser conf@LdapConf{..} pool Creds{..} = liftIO . (`catches` errHandlers) $ , Exc.Handler $ \(HostCannotConnect host excs) -> throwM $ CampusUserHostCannotConnect host excs ] -campusUser' :: (MonadCatch m, MonadUnliftIO m) => LdapConf -> LdapPool -> User -> m (Maybe (Ldap.AttrList [])) -campusUser' conf pool User{userIdent} - = runMaybeT . catchIfMaybeT (is _CampusUserNoResult) $ campusUser conf pool (Creds apLdap (CI.original userIdent) []) +campusUserReTest :: MonadUnliftIO m => Failover (LdapConf, LdapPool) -> (Nano -> Bool) -> FailoverMode -> Creds site -> m (Ldap.AttrList []) +campusUserReTest pool doTest = campusUserWith (\l -> flip (withLdapFailoverReTest l) doTest) pool + +campusUserReTest' :: (MonadCatch m, MonadUnliftIO m) => Failover (LdapConf, LdapPool) -> (Nano -> Bool) -> FailoverMode -> User -> m (Maybe (Ldap.AttrList [])) +campusUserReTest' pool doTest mode User{userIdent} + = runMaybeT . catchIfMaybeT (is _CampusUserNoResult) $ campusUserReTest pool doTest mode (Creds apLdap (CI.original userIdent) []) + +campusUser :: MonadUnliftIO m => Failover (LdapConf, LdapPool) -> FailoverMode -> Creds site -> m (Ldap.AttrList []) +campusUser = campusUserWith withLdapFailover + +campusUser' :: (MonadCatch m, MonadUnliftIO m) => Failover (LdapConf, LdapPool) -> FailoverMode -> User -> m (Maybe (Ldap.AttrList [])) +campusUser' pool mode User{userIdent} + = runMaybeT . catchIfMaybeT (is _CampusUserNoResult) $ campusUser pool mode (Creds apLdap (CI.original userIdent) []) -campusUserMatr :: MonadUnliftIO m => LdapConf -> LdapPool -> UserMatriculation -> m (Ldap.AttrList []) -campusUserMatr conf@LdapConf{..} pool userMatr = liftIO . (`catches` errHandlers) $ either (throwM . CampusUserLdapError) return <=< withLdap pool $ \ldap -> do +campusUserMatr :: MonadUnliftIO m => Failover (LdapConf, LdapPool) -> FailoverMode -> UserMatriculation -> m (Ldap.AttrList []) +campusUserMatr pool mode userMatr = liftIO . (`catches` errHandlers) $ either (throwM . CampusUserLdapError) return <=< withLdapFailover _2 pool mode $ \(conf@LdapConf{..}, ldap) -> do Ldap.bind ldap ldapDn ldapPassword results <- findUserMatr conf ldap userMatr [] case results of @@ -140,9 +161,9 @@ campusUserMatr conf@LdapConf{..} pool userMatr = liftIO . (`catches` errHandlers , Exc.Handler $ \(HostCannotConnect host excs) -> throwM $ CampusUserHostCannotConnect host excs ] -campusUserMatr' :: (MonadCatch m, MonadUnliftIO m) => LdapConf -> LdapPool -> UserMatriculation -> m (Maybe (Ldap.AttrList [])) -campusUserMatr' conf pool - = runMaybeT . catchIfMaybeT (is _CampusUserNoResult) . campusUserMatr conf pool +campusUserMatr' :: (MonadCatch m, MonadUnliftIO m) => Failover (LdapConf, LdapPool) -> FailoverMode -> UserMatriculation -> m (Maybe (Ldap.AttrList [])) +campusUserMatr' pool mode + = runMaybeT . catchIfMaybeT (is _CampusUserNoResult) . campusUserMatr pool mode @@ -168,8 +189,8 @@ campusLogin :: forall site. , RenderMessage site CampusMessage , RenderMessage site AFormMessage , Button site ButtonSubmit - ) => LdapConf -> LdapPool -> AuthPlugin site -campusLogin conf@LdapConf{..} pool = AuthPlugin{..} + ) => Failover (LdapConf, LdapPool) -> FailoverMode -> AuthPlugin site +campusLogin pool mode = AuthPlugin{..} where apName :: Text apName = apLdap @@ -184,7 +205,7 @@ campusLogin conf@LdapConf{..} pool = AuthPlugin{..} redirect $ tp LoginR FormMissing -> redirect $ tp LoginR FormSuccess CampusLogin{ campusIdent = CI.original -> campusIdent, ..} -> do - ldapResult <- withLdap pool $ \ldap -> liftIO $ do + ldapResult <- withLdapFailover _2 pool mode $ \(conf@LdapConf{..}, ldap) -> liftIO $ do Ldap.bind ldap ldapDn ldapPassword searchResults <- findUser conf ldap campusIdent [ldapUserPrincipalName] case searchResults of diff --git a/src/Crypto/Hash/Instances.hs b/src/Crypto/Hash/Instances.hs index ae803ae92..27304d542 100644 --- a/src/Crypto/Hash/Instances.hs +++ b/src/Crypto/Hash/Instances.hs @@ -43,3 +43,6 @@ instance HashAlgorithm hash => ToJSON (Digest hash) where instance HashAlgorithm hash => FromJSON (Digest hash) where parseJSON = withText "Digest" $ either (fail . unpack) return . parseUrlPiece + +instance Hashable (Digest hash) where + hashWithSalt s = (hashWithSalt s :: ByteString -> Int) . convert diff --git a/src/Crypto/Random/Instances.hs b/src/Crypto/Random/Instances.hs new file mode 100644 index 000000000..068760c2b --- /dev/null +++ b/src/Crypto/Random/Instances.hs @@ -0,0 +1,19 @@ +{-# OPTIONS_GHC -fno-warn-orphans #-} + +module Crypto.Random.Instances + ( + ) where + +import ClassyPrelude + +import Crypto.Random +import System.Random (RandomGen(..)) + +import qualified Data.ByteArray as BA + +import Data.Bits + + +instance RandomGen ChaChaDRG where + next g = withRandomBytes g (finiteBitSize (maxBound :: Int) `div` 8) (foldr (\x acc -> acc `shiftL` 8 .|. fromIntegral x) zeroBits . BA.unpack @BA.Bytes) + split g = withDRG g drgNew diff --git a/src/CryptoID.hs b/src/CryptoID.hs index c7b8618cc..02ec64b11 100644 --- a/src/CryptoID.hs +++ b/src/CryptoID.hs @@ -15,9 +15,9 @@ import Model import CryptoID.TH import qualified Data.CryptoID as E -import Data.CryptoID.Poly.ImplicitNamespace -import Data.UUID.Cryptographic.ImplicitNamespace -import System.FilePath.Cryptographic.ImplicitNamespace +import Data.CryptoID.Poly.ImplicitNamespace hiding (decrypt, encrypt) +import Data.UUID.Cryptographic.ImplicitNamespace hiding (decrypt, encrypt) +import System.FilePath.Cryptographic.ImplicitNamespace hiding (decrypt, encrypt) import qualified Data.Text as Text @@ -28,6 +28,28 @@ import Data.Aeson.Encoding (text) import Text.Blaze (ToMarkup(..)) +import qualified Data.CryptoID.Class.ImplicitNamespace as I + + +encrypt :: forall plaintext ciphertext m. + ( I.HasCryptoID ciphertext plaintext m + , KnownSymbol (CryptoIDNamespace ciphertext plaintext) + , MonadHandler m + , Typeable ciphertext + , PathPiece plaintext + ) + => plaintext -> m (I.CryptoID ciphertext plaintext) +encrypt plain = $cachedHereBinary (toPathPiece plain) $ I.encrypt plain + +decrypt :: forall plaintext ciphertext m. + ( I.HasCryptoID ciphertext plaintext m + , MonadHandler m + , Typeable plaintext + , PathPiece ciphertext + ) + => I.CryptoID ciphertext plaintext -> m plaintext +decrypt cipher = $cachedHereBinary (toPathPiece $ ciphertext cipher) $ I.decrypt cipher + instance {-# OVERLAPPING #-} MonadThrow m => MonadCrypto (ReaderT CryptoIDKey m) where type MonadCryptoKey (ReaderT CryptoIDKey m) = CryptoIDKey diff --git a/src/Data/Aeson/Types/Instances.hs b/src/Data/Aeson/Types/Instances.hs index 10d4c106e..66849e5a5 100644 --- a/src/Data/Aeson/Types/Instances.hs +++ b/src/Data/Aeson/Types/Instances.hs @@ -9,11 +9,6 @@ import ClassyPrelude import Data.Aeson.Types (Parser, Value) import Control.Monad.Catch -import Data.Binary (Binary) - -import Data.HashMap.Strict.Instances () -import Data.Vector.Instances () - import Model.Types.TH.JSON (derivePersistFieldJSON) import Control.Monad.Fail @@ -22,7 +17,5 @@ import Control.Monad.Fail instance MonadThrow Parser where throwM = fail . show -instance Binary Value - derivePersistFieldJSON ''Value diff --git a/src/Data/CaseInsensitive/Instances.hs b/src/Data/CaseInsensitive/Instances.hs index 77f626338..6596fe47e 100644 --- a/src/Data/CaseInsensitive/Instances.hs +++ b/src/Data/CaseInsensitive/Instances.hs @@ -26,9 +26,6 @@ import qualified Database.Esqueleto as E import Web.HttpApiData -import Data.Binary (Binary) -import qualified Data.Binary as Binary - import qualified Data.Csv as Csv @@ -99,11 +96,6 @@ instance (CI.FoldCase s, PathMultiPiece s) => PathMultiPiece (CI s) where fromPathMultiPiece = fmap CI.mk . fromPathMultiPiece toPathMultiPiece = toPathMultiPiece . CI.original -instance (CI.FoldCase s, Binary s) => Binary (CI s) where - get = CI.mk <$> Binary.get - put = Binary.put . CI.original - putList = Binary.putList . map CI.original - instance Csv.ToField s => Csv.ToField (CI s) where toField = Csv.toField . CI.original diff --git a/src/Data/HashMap/Strict/Instances.hs b/src/Data/HashMap/Strict/Instances.hs deleted file mode 100644 index daa36c68a..000000000 --- a/src/Data/HashMap/Strict/Instances.hs +++ /dev/null @@ -1,15 +0,0 @@ -{-# OPTIONS_GHC -fno-warn-orphans #-} - -module Data.HashMap.Strict.Instances - ( - ) where - -import ClassyPrelude - -import Data.Binary (Binary(..)) -import qualified Data.HashMap.Strict as HashMap - - -instance (Binary k, Binary v, Hashable k, Eq k) => Binary (HashMap k v) where - put = put . HashMap.toList - get = HashMap.fromList <$> get diff --git a/src/Data/HashSet/Instances.hs b/src/Data/HashSet/Instances.hs deleted file mode 100644 index 320ac8940..000000000 --- a/src/Data/HashSet/Instances.hs +++ /dev/null @@ -1,16 +0,0 @@ -{-# OPTIONS_GHC -fno-warn-orphans #-} - -module Data.HashSet.Instances - ( - ) where - -import ClassyPrelude - -import qualified Data.HashSet as HashSet - -import Data.Binary (Binary(..)) - - -instance (Binary a, Hashable a, Eq a) => Binary (HashSet a) where - get = HashSet.fromList <$> get - put = put . HashSet.toList diff --git a/src/Data/MonoTraversable/Instances.hs b/src/Data/MonoTraversable/Instances.hs new file mode 100644 index 000000000..13405c291 --- /dev/null +++ b/src/Data/MonoTraversable/Instances.hs @@ -0,0 +1,38 @@ +{-# OPTIONS_GHC -fno-warn-orphans #-} + +module Data.MonoTraversable.Instances + () where + +import ClassyPrelude + +import Data.Monoid (Any(..), All(..)) + + +type instance Element Any = Bool +type instance Element All = Bool + +instance MonoFunctor Any where + omap f = Any . f . getAny + +instance MonoFunctor All where + omap f = All . f . getAll + +instance MonoPointed Any where + opoint = Any + +instance MonoPointed All where + opoint = All + +instance MonoFoldable Any where + ofoldMap f = f . getAny + ofoldr f x (Any b) = f b x + ofoldl' f x (Any b) = f x b + ofoldr1Ex _ = getAny + ofoldl1Ex' _ = getAny + +instance MonoFoldable All where + ofoldMap f = f . getAll + ofoldr f x (All b) = f b x + ofoldl' f x (All b) = f x b + ofoldr1Ex _ = getAll + ofoldl1Ex' _ = getAll diff --git a/src/Data/NonNull/Instances.hs b/src/Data/NonNull/Instances.hs index 8c7c3dca8..ad472219a 100644 --- a/src/Data/NonNull/Instances.hs +++ b/src/Data/NonNull/Instances.hs @@ -28,3 +28,7 @@ instance Hashable a => Hashable (NonNull a) where instance (Binary a, MonoFoldable a) => Binary (NonNull a) where get = Binary.get >>= maybe (fail "Expected non-empty structure") return . fromNullable put = Binary.put . toNullable + + +instance NFData a => NFData (NonNull a) where + rnf = rnf . toNullable diff --git a/src/Data/Time/Calendar/Instances.hs b/src/Data/Time/Calendar/Instances.hs index d5dd127ed..15c77e94b 100644 --- a/src/Data/Time/Calendar/Instances.hs +++ b/src/Data/Time/Calendar/Instances.hs @@ -6,7 +6,6 @@ module Data.Time.Calendar.Instances ) where import ClassyPrelude -import Data.Binary (Binary) import Data.Time.Calendar @@ -14,7 +13,6 @@ import Data.Universe deriving newtype instance Hashable Day -deriving newtype instance Binary Day deriving instance Ord DayOfWeek instance Universe DayOfWeek where diff --git a/src/Data/Time/Clock/Instances.hs b/src/Data/Time/Clock/Instances.hs index d1b0af22e..fa152557f 100644 --- a/src/Data/Time/Clock/Instances.hs +++ b/src/Data/Time/Clock/Instances.hs @@ -1,8 +1,7 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} module Data.Time.Clock.Instances - ( iso8601OutputFormat, iso8601ParseFormat - ) where + () where import ClassyPrelude @@ -10,19 +9,21 @@ import Database.Persist.Sql import Data.Proxy -import Data.Binary (Binary) -import qualified Data.Binary as Binary - import Data.Time.Clock import Data.Time.Calendar.Instances () import Web.PathPieces import qualified Data.Csv as Csv +import Data.Time.Format.ISO8601 + instance Hashable DiffTime where hashWithSalt s = hashWithSalt s . toRational +instance Hashable NominalDiffTime where + hashWithSalt s = hashWithSalt s . toRational + instance PersistField NominalDiffTime where toPersistValue = toPersistValue . toRational fromPersistValue = fmap fromRational . fromPersistValue @@ -31,27 +32,15 @@ instance PersistFieldSql NominalDiffTime where sqlType _ = sqlType (Proxy @Rational) -iso8601OutputFormat, iso8601ParseFormat :: String -iso8601OutputFormat = "%0Y-%m-%dT%H:%M:%S%Q%z" -iso8601ParseFormat = "%Y-%m-%dT%H:%M:%S%Q%z" - - deriving instance Generic UTCTime instance Hashable UTCTime instance PathPiece UTCTime where - toPathPiece = pack . formatTime defaultTimeLocale iso8601OutputFormat - fromPathPiece = parseTimeM False defaultTimeLocale iso8601ParseFormat . unpack + toPathPiece = pack . iso8601Show + fromPathPiece = iso8601ParseM . unpack instance Csv.ToField UTCTime where - toField = Csv.toField . formatTime defaultTimeLocale iso8601OutputFormat + toField = Csv.toField . iso8601Show instance Csv.FromField UTCTime where - parseField = parseTimeM False defaultTimeLocale iso8601ParseFormat <=< Csv.parseField - - -instance Binary DiffTime where - get = fromRational <$> Binary.get - put = Binary.put . toRational - -instance Binary UTCTime + parseField = iso8601ParseM <=< Csv.parseField diff --git a/src/Data/Time/LocalTime/Instances.hs b/src/Data/Time/LocalTime/Instances.hs index 6bdf4610d..210bc7f62 100644 --- a/src/Data/Time/LocalTime/Instances.hs +++ b/src/Data/Time/LocalTime/Instances.hs @@ -8,28 +8,13 @@ import ClassyPrelude import Data.Time.LocalTime -import Data.Binary (Binary) - import qualified Language.Haskell.TH.Syntax as TH -import qualified Data.Csv as Csv - -import Data.Time.Clock.Instances - ( iso8601OutputFormat, iso8601ParseFormat - ) - deriving instance Generic TimeOfDay deriving instance Typeable TimeOfDay instance Hashable TimeOfDay -instance Binary TimeOfDay deriving instance TH.Lift TimeZone - -instance Csv.ToField ZonedTime where - toField = Csv.toField . formatTime defaultTimeLocale iso8601OutputFormat - -instance Csv.FromField ZonedTime where - parseField = parseTimeM False defaultTimeLocale iso8601ParseFormat <=< Csv.parseField diff --git a/src/Data/Vector/Instances.hs b/src/Data/Vector/Instances.hs deleted file mode 100644 index ecb64bd69..000000000 --- a/src/Data/Vector/Instances.hs +++ /dev/null @@ -1,17 +0,0 @@ -{-# OPTIONS_GHC -fno-warn-orphans #-} - -module Data.Vector.Instances - ( - ) where - -import ClassyPrelude - -import qualified Data.Vector as Vector - -import Data.Binary (Binary) -import qualified Data.Binary as Binary - - -instance Binary a => Binary (Vector a) where - get = Vector.fromList <$> Binary.get - put = Binary.put . Vector.toList diff --git a/src/Database/Persist/Class/Instances.hs b/src/Database/Persist/Class/Instances.hs index a44e6071b..4a3a7208c 100644 --- a/src/Database/Persist/Class/Instances.hs +++ b/src/Database/Persist/Class/Instances.hs @@ -13,6 +13,7 @@ import Database.Persist.Sql import Data.Binary (Binary) import qualified Data.Binary as Binary +import Data.Binary.Instances () import qualified Data.Map as Map diff --git a/src/Database/Persist/Types/Instances.hs b/src/Database/Persist/Types/Instances.hs index 0929a2886..e7309b6cc 100644 --- a/src/Database/Persist/Types/Instances.hs +++ b/src/Database/Persist/Types/Instances.hs @@ -11,6 +11,7 @@ import Database.Persist.Types import Data.Time.Calendar.Instances () import Data.Time.LocalTime.Instances () import Data.Time.Clock.Instances () +import Data.Binary.Instances () import Data.Binary (Binary) diff --git a/src/Foundation.hs b/src/Foundation.hs index 6596c912c..4f5ae399e 100644 --- a/src/Foundation.hs +++ b/src/Foundation.hs @@ -9,6 +9,7 @@ module Foundation ) where import Foundation.Type as Foundation +import Foundation.Types as Foundation import Foundation.I18n as Foundation import Foundation.Routes as Foundation @@ -22,7 +23,9 @@ import Auth.LDAP import Auth.PWHash import Auth.Dummy -import qualified Network.Wai as W (pathInfo) +import qualified Network.Wai as W +import qualified Network.HTTP.Types.Header as W +import qualified Network.Wai.Middleware.HttpAuth as W (extractBearerAuth) import Yesod.Core.Types (HandlerContents) import qualified Yesod.Core.Unsafe as Unsafe @@ -46,19 +49,18 @@ import qualified Data.Set as Set import Data.Map ((!?)) import qualified Data.Map as Map import qualified Data.HashSet as HashSet +import qualified Data.HashMap.Strict as HashMap import qualified Data.List.NonEmpty as NonEmpty import Data.List ((!!), findIndex, inits) import qualified Data.List as List -import Web.Cookie - import Data.Conduit.List (sourceList) import qualified Database.Esqueleto as E import qualified Database.Esqueleto.Utils as E -import Control.Monad.Except (MonadError(..), ExceptT) +import Control.Monad.Except (MonadError(..)) import Control.Monad.Trans.State (execStateT) import Control.Monad.Writer.Class (MonadWriter(..)) import Control.Monad.Memo.Class (MonadMemo(..), for4) @@ -71,9 +73,11 @@ import Handler.Utils.ExamOffice.ExternalExam import Handler.Utils.ExamOffice.Course import Handler.Utils.Profile import Handler.Utils.Routes +import Handler.Utils.Memcached import Utils.Form import Utils.Sheet import Utils.SystemMessage +import Utils.Metrics import Text.Cassius (cassiusFile) @@ -96,6 +100,11 @@ import qualified Ldap.Client as Ldap import UnliftIO.Pool +import qualified Web.ServerSession.Core as ServerSession +import qualified Web.ServerSession.Frontend.Yesod.Jwt as JwtSession + +import Web.Cookie + -- | Convenient Type Synonyms: type DB = YesodDB UniWorX type Form x = Html -> MForm (HandlerFor UniWorX) (FormResult x, Widget) @@ -114,8 +123,7 @@ data NavQuickView = NavQuickViewFavourite | NavQuickViewPageActionSecondary deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic, Typeable) -instance Universe NavQuickView -instance Finite NavQuickView + deriving (Universe, Finite) navQuick :: NavQuickView -> (NavQuickView -> Any) navQuick x x' = Any $ x == x' @@ -127,7 +135,9 @@ data NavType | NavTypeButton { navMethod :: StdMethod , navData :: [(Text, Text)] - } deriving (Eq, Ord, Read, Show, Generic, Typeable) + } + deriving (Eq, Ord, Read, Show, Generic, Typeable) + deriving anyclass (Binary) makeLenses_ ''NavType makePrisms ''NavType @@ -204,7 +214,10 @@ navLinkAccess NavLink{..} = handle shortCircuit $ liftHandler navAccess' `and2M` shortCircuit _ = return False accessCheck :: HasRoute UniWorX route => NavType -> route -> m Bool - accessCheck nt (urlRoute -> route) = bool hasWriteAccessTo hasReadAccessTo (is _NavTypeLink nt) route + accessCheck nt (urlRoute -> route) = do + authCtx <- getAuthContext + $memcachedByHere (Just $ Right 120) (authCtx, nt, route) $ + bool hasWriteAccessTo hasReadAccessTo (is _NavTypeLink nt) route getTimeLocale' :: [Lang] -> TimeLocale @@ -297,53 +310,80 @@ trueAP = APPure . const . const . const $ trueAR <$> ask falseAP = APPure . const . const . const $ falseAR <$> ask -- included for completeness -askTokenUnsafe :: forall m. +data AuthContext = AuthContext + { authCtxAuth :: Maybe UserId + , authCtxBearer :: Maybe (BearerToken UniWorX) + , authActiveTags :: AuthTagActive + } deriving (Eq, Read, Show, Generic, Typeable) + deriving anyclass (Hashable, Binary) + +getAuthContext :: forall m. ( MonadHandler m , HandlerSite m ~ UniWorX , MonadCatch m ) - => ExceptT AuthResult m (BearerToken (UniWorX)) + => m AuthContext +getAuthContext = do + authCtx <- AuthContext + <$> maybeAuthId + <*> runMaybeT (exceptTMaybe askBearerUnsafe) + <*> (fromMaybe def <$> lookupSessionJson SessionActiveAuthTags) + + $logDebugS "getAuthContext" $ tshow authCtx + + return authCtx + + +askBearerUnsafe :: forall m. + ( MonadHandler m + , HandlerSite m ~ UniWorX + , MonadCatch m + ) + => ExceptT AuthResult m (BearerToken UniWorX) -- | This performs /no/ meaningful validation of the `BearerToken` -- -- Use `Handler.Utils.Tokens.requireBearerToken` or `Handler.Utils.Tokens.maybeBearerToken` instead -askTokenUnsafe = $cachedHere $ do - jwt <- maybeMExceptT (unauthorizedI MsgUnauthorizedNoToken) askJwt - catch (decodeToken jwt) $ \case +askBearerUnsafe = $cachedHere $ do + bearer <- maybeMExceptT (unauthorizedI MsgUnauthorizedNoToken) askBearer + catch (decodeBearer bearer) $ \case BearerTokenExpired -> throwError =<< unauthorizedI MsgUnauthorizedTokenExpired BearerTokenNotStarted -> throwError =<< unauthorizedI MsgUnauthorizedTokenNotStarted other -> do $logWarnS "AuthToken" $ tshow other throwError =<< unauthorizedI MsgUnauthorizedTokenInvalid -validateToken :: Maybe (AuthId UniWorX) -> Route UniWorX -> Bool -> BearerToken UniWorX -> DB AuthResult -validateToken mAuthId' route' isWrite' token' = $runCachedMemoT $ for4 memo validateToken' mAuthId' route' isWrite' token' +validateBearer :: Maybe (AuthId UniWorX) -> Route UniWorX -> Bool -> BearerToken UniWorX -> DB AuthResult +validateBearer mAuthId' route' isWrite' token' = $runCachedMemoT $ for4 memo validateBearer' mAuthId' route' isWrite' token' where - validateToken' :: _ -> _ -> _ -> _ -> CachedMemoT (Maybe (AuthId UniWorX), Route UniWorX, Bool, BearerToken UniWorX) AuthResult DB AuthResult - validateToken' mAuthId route isWrite BearerToken{..} = lift . exceptT return return $ do - guardMExceptT (maybe True (HashSet.member route) tokenRoutes) (unauthorizedI MsgUnauthorizedTokenInvalidRoute) + validateBearer' :: _ -> _ -> _ -> _ -> CachedMemoT (Maybe (AuthId UniWorX), Route UniWorX, Bool, BearerToken UniWorX) AuthResult DB AuthResult + validateBearer' mAuthId route isWrite BearerToken{..} = lift . exceptT return return $ do + guardMExceptT (maybe True (HashSet.member route) bearerRoutes) (unauthorizedI MsgUnauthorizedTokenInvalidRoute) - tokenAuthority' <- case tokenAuthority of + bearerAuthority' <- flip foldMapM bearerAuthority $ \case Left tVal | JSON.Success groupName <- JSON.fromJSON tVal -> maybeT (throwError =<< unauthorizedI MsgUnauthorizedTokenInvalidAuthorityGroup) . hoist lift $ do Entity _ UserGroupMember{..} <- MaybeT . getBy $ UniquePrimaryUserGroupMember groupName Active - return userGroupMemberUser + return $ Set.singleton userGroupMemberUser | otherwise -> throwError =<< unauthorizedI MsgUnauthorizedTokenInvalidAuthorityValue - Right uid -> return uid - - User{userTokensIssuedAfter} <- maybeMExceptT (unauthorizedI MsgUnauthorizedTokenInvalidAuthority) $ get tokenAuthority' - guardMExceptT (Just tokenIssuedAt >= userTokensIssuedAfter) (unauthorizedI MsgUnauthorizedTokenExpired) + Right uid -> return $ Set.singleton uid let -- Prevent infinite loops noTokenAuth :: AuthDNF -> AuthDNF noTokenAuth = over _dnfTerms . Set.filter . noneOf (re _nullable . folded) $ (== AuthToken) . plVar - authorityVal <- do - dnf <- either throwM return $ routeAuthTags route - fmap fst . runWriterT $ evalAuthTags (AuthTagActive $ const True) (noTokenAuth dnf) (Just tokenAuthority') route isWrite - guardExceptT (is _Authorized authorityVal) authorityVal + guardMExceptT (not $ Set.null bearerAuthority') $ unauthorizedI MsgUnauthorizedTokenInvalidNoAuthority - whenIsJust tokenAddAuth $ \addDNF -> do + forM_ bearerAuthority' $ \uid -> do + User{userTokensIssuedAfter} <- maybeMExceptT (unauthorizedI MsgUnauthorizedTokenInvalidAuthority) $ get uid + guardMExceptT (Just bearerIssuedAt >= userTokensIssuedAfter) (unauthorizedI MsgUnauthorizedTokenExpired) + + authorityVal <- do + dnf <- either throwM return $ routeAuthTags route + fmap fst . runWriterT $ evalAuthTags (AuthTagActive $ const True) (noTokenAuth dnf) (Just uid) route isWrite + guardExceptT (is _Authorized authorityVal) authorityVal + + whenIsJust bearerAddAuth $ \addDNF -> do $logDebugS "validateToken" $ tshow addDNF additionalVal <- fmap fst . runWriterT $ evalAuthTags (AuthTagActive $ const True) (noTokenAuth addDNF) mAuthId route isWrite guardExceptT (is _Authorized additionalVal) additionalVal @@ -446,13 +486,13 @@ tagAccessPredicate AuthAllocationAdmin = APDB $ \mAuthId route _ -> case route o guardMExceptT isEvaluation $ unauthorizedI MsgUnauthorizedAllocationAdmin return Authorized tagAccessPredicate AuthToken = APDB $ \mAuthId route isWrite -> exceptT return return $ - lift . validateToken mAuthId route isWrite =<< askTokenUnsafe + lift . validateBearer mAuthId route isWrite =<< askBearerUnsafe tagAccessPredicate AuthNoEscalation = APDB $ \mAuthId route _ -> case route of - AdminHijackUserR cID -> exceptT return return $ do + AdminHijackUserR cID -> $cachedHereBinary (mAuthId, cID) . exceptT return return $ do myUid <- maybeExceptT AuthenticationRequired $ return mAuthId uid <- decrypt cID - otherSchoolsFunctions <- lift $ Set.fromList . map (userFunctionSchool . entityVal) <$> selectList [UserFunctionUser ==. uid] [] - mySchools <- lift $ Set.fromList . map (userFunctionSchool . entityVal) <$> selectList [UserFunctionUser ==. myUid, UserFunctionFunction ==. SchoolAdmin] [] + otherSchoolsFunctions <- lift . $cachedHereBinary uid $ Set.fromList . map (userFunctionSchool . entityVal) <$> selectList [UserFunctionUser ==. uid] [] + mySchools <- lift . $cachedHereBinary myUid $ Set.fromList . map (userFunctionSchool . entityVal) <$> selectList [UserFunctionUser ==. myUid, UserFunctionFunction ==. SchoolAdmin] [] guardMExceptT (otherSchoolsFunctions `Set.isSubsetOf` mySchools) (unauthorizedI MsgUnauthorizedAdminEscalation) return Authorized r -> $unsupportedAuthPredicate AuthNoEscalation r @@ -509,7 +549,7 @@ tagAccessPredicate AuthLecturer = APDB $ \mAuthId route _ -> case route of return Authorized tagAccessPredicate AuthCorrector = APDB $ \mAuthId route _ -> exceptT return return $ do authId <- maybeExceptT AuthenticationRequired $ return mAuthId - resList <- $cachedHereBinary (mAuthId) . lift . E.select . E.from $ \(course `E.InnerJoin` sheet `E.InnerJoin` sheetCorrector) -> do + resList <- $cachedHereBinary mAuthId . lift . E.select . E.from $ \(course `E.InnerJoin` sheet `E.InnerJoin` sheetCorrector) -> do E.on $ sheetCorrector E.^. SheetCorrectorSheet E.==. sheet E.^. SheetId E.on $ sheet E.^. SheetCourse E.==. course E.^. CourseId E.where_ $ sheetCorrector E.^. SheetCorrectorUser E.==. E.val authId @@ -536,9 +576,9 @@ tagAccessPredicate AuthCorrector = APDB $ \mAuthId route _ -> exceptT return ret guardMExceptT (not $ Map.null resMap) (unauthorizedI MsgUnauthorizedCorrectorAny) return Authorized tagAccessPredicate AuthExamCorrector = APDB $ \mAuthId route _ -> case route of - CExamR tid ssh csh examn _ -> exceptT return return $ do + CExamR tid ssh csh examn _ -> $cachedHereBinary (mAuthId, tid, ssh, csh, examn) . exceptT return return $ do authId <- maybeExceptT AuthenticationRequired $ return mAuthId - isCorrector <- $cachedHereBinary (mAuthId, tid, ssh, csh, examn) . lift . E.selectExists . E.from $ \(course `E.InnerJoin` exam `E.InnerJoin` examCorrector) -> do + isCorrector <- lift . E.selectExists . E.from $ \(course `E.InnerJoin` exam `E.InnerJoin` examCorrector) -> do E.on $ examCorrector E.^. ExamCorrectorExam E.==. exam E.^. ExamId E.&&. examCorrector E.^. ExamCorrectorUser E.==. E.val authId E.on $ exam E.^. ExamCourse E.==. course E.^. CourseId @@ -579,6 +619,29 @@ tagAccessPredicate AuthTutorControl = APDB $ \_ route _ -> case route of guard tutorialTutorControlled return Authorized r -> $unsupportedAuthPredicate AuthTutorControl r +tagAccessPredicate AuthSubmissionGroup = APDB $ \mAuthId route _ -> case route of + CSubmissionR _ _ _ _ cID _ -> maybeT (unauthorizedI MsgUnauthorizedSubmissionSubmissionGroup) $ do + smId <- catchIfMaybeT (const True :: CryptoIDError -> Bool) $ decrypt cID + groups <- $cachedHereBinary cID . lift . fmap (Set.fromList . fmap E.unValue) . E.select . E.from $ \(submissionGroupUser `E.InnerJoin` submissionUser) -> do + E.on $ submissionGroupUser E.^. SubmissionGroupUserUser E.==. submissionUser E.^. SubmissionUserUser + E.where_ $ submissionUser E.^. SubmissionUserSubmission E.==. E.val smId + return $ submissionGroupUser E.^. SubmissionGroupUserSubmissionGroup + unless (Set.null groups) $ do + uid <- hoistMaybe mAuthId + guardM . lift $ exists [SubmissionGroupUserUser ==. uid, SubmissionGroupUserSubmissionGroup <-. Set.toList groups] + return Authorized + CSheetR tid ssh csh sheetn _ -> maybeT (unauthorizedI MsgUnauthorizedSheetSubmissionGroup) $ do + course <- $cachedHereBinary (tid, ssh, csh) . MaybeT . getKeyBy $ TermSchoolCourseShort tid ssh csh + Entity _ Sheet{..} <- $cachedHereBinary (course, sheetn) . MaybeT . getBy $ CourseSheet course sheetn + when (is _RegisteredGroups sheetGrouping) $ do + uid <- hoistMaybe mAuthId + guardM . lift . E.selectExists . E.from $ \(submissionGroup `E.InnerJoin` submissionGroupUser) -> do + E.on $ submissionGroupUser E.^. SubmissionGroupUserSubmissionGroup E.==. submissionGroup E.^. SubmissionGroupId + E.where_ $ submissionGroup E.^. SubmissionGroupCourse E.==. E.val course + E.&&. submissionGroupUser E.^. SubmissionGroupUserUser E.==. E.val uid + + return Authorized + r -> $unsupportedAuthPredicate AuthSubmissionGroup r tagAccessPredicate AuthTime = APDB $ \mAuthId route _ -> case route of CApplicationR tid ssh csh _ _ -> maybeT (unauthorizedI MsgUnauthorizedApplicationTime) $ do course <- $cachedHereBinary (tid, ssh, csh) . MaybeT . getKeyBy $ TermSchoolCourseShort tid ssh csh @@ -694,7 +757,7 @@ tagAccessPredicate AuthTime = APDB $ \mAuthId route _ -> case route of now <- liftIO getCurrentTime mbc <- $cachedHereBinary (tid, ssh, csh) . getBy $ TermSchoolCourseShort tid ssh csh registered <- case (mbc,mAuthId) of - (Just (Entity cid _), Just uid) -> $cachedHereBinary (uid, cid) $ isJust <$> (getBy $ UniqueParticipant uid cid) + (Just (Entity cid _), Just uid) -> $cachedHereBinary (uid, cid) $ exists [CourseParticipantUser ==. uid, CourseParticipantCourse ==. cid, CourseParticipantState ==. CourseParticipantActive] _ -> return False case mbc of (Just (Entity _ Course{courseRegisterFrom, courseRegisterTo})) @@ -759,6 +822,14 @@ tagAccessPredicate AuthTime = APDB $ \mAuthId route _ -> case route of && NTop systemMessageTo >= cTime return Authorized + MessageHideR cID -> maybeT (unauthorizedI MsgUnauthorizedSystemMessageTime) $ do + smId <- catchIfMaybeT (const True :: CryptoIDError -> Bool) $ decrypt cID + SystemMessage{systemMessageFrom, systemMessageTo} <- $cachedHereBinary smId . MaybeT $ get smId + cTime <- (NTop . Just) <$> liftIO getCurrentTime + guard $ NTop systemMessageFrom <= cTime + && NTop systemMessageTo >= cTime + return Authorized + CNewsR _ _ _ cID _ -> maybeT (unauthorizedI MsgUnauthorizedCourseNewsTime) $ do nId <- catchIfMaybeT (const True :: CryptoIDError -> Bool) $ decrypt cID CourseNews{courseNewsVisibleFrom} <- $cachedHereBinary nId . MaybeT $ get nId @@ -844,6 +915,7 @@ tagAccessPredicate AuthCourseRegistered = APDB $ \mAuthId route _ -> case route isRegistered <- $cachedHereBinary (authId, tid, ssh, csh) . lift . E.selectExists . E.from $ \(course `E.InnerJoin` courseParticipant) -> do E.on $ course E.^. CourseId E.==. courseParticipant E.^. CourseParticipantCourse E.where_ $ courseParticipant E.^. CourseParticipantUser E.==. E.val authId + E.&&. courseParticipant E.^. CourseParticipantState E.==. E.val CourseParticipantActive E.&&. course E.^. CourseTerm E.==. E.val tid E.&&. course E.^. CourseSchool E.==. E.val ssh E.&&. course E.^. CourseShorthand E.==. E.val csh @@ -1019,48 +1091,52 @@ tagAccessPredicate AuthParticipant = APDB $ \mAuthId route _ -> case route of CourseNews{courseNewsParticipantsOnly} <- $cachedHereBinary nId . MaybeT $ get nId if | courseNewsParticipantsOnly -> do uid <- hoistMaybe mAuthId - exceptT return (const mzero) . hoist lift $ isCourseParticipant tid ssh csh uid + exceptT return (const mzero) . hoist lift $ isCourseParticipant tid ssh csh uid True | otherwise -> return Authorized CourseR tid ssh csh (CUserR cID) -> exceptT return return $ do participant <- catchIfMExceptT (const $ unauthorizedI MsgUnauthorizedParticipant) (const True :: CryptoIDError -> Bool) $ decrypt cID - isCourseParticipant tid ssh csh participant + isCourseParticipant tid ssh csh participant False unauthorizedI MsgUnauthorizedParticipant r -> $unsupportedAuthPredicate AuthParticipant r where - isCourseParticipant tid ssh csh participant = do + isCourseParticipant tid ssh csh participant onlyActive = do let authorizedIfExists :: E.From a => (a -> E.SqlQuery b) -> ExceptT AuthResult DB () authorizedIfExists = flip whenExceptT Authorized <=< lift . E.selectExists . E.from -- participant is currently registered - $cachedHereBinary (participant, tid, ssh, csh) . authorizedIfExists $ \(course `E.InnerJoin` courseParticipant) -> do + mapExceptT ($cachedHereBinary (participant, tid, ssh, csh)) . authorizedIfExists $ \(course `E.InnerJoin` courseParticipant) -> do E.on $ course E.^. CourseId E.==. courseParticipant E.^. CourseParticipantCourse E.where_ $ courseParticipant E.^. CourseParticipantUser E.==. E.val participant E.&&. course E.^. CourseTerm E.==. E.val tid E.&&. course E.^. CourseSchool E.==. E.val ssh E.&&. course E.^. CourseShorthand E.==. E.val csh + when onlyActive $ + E.where_ $ courseParticipant E.^. CourseParticipantState E.==. E.val CourseParticipantActive -- participant has at least one submission - $cachedHereBinary (participant, tid, ssh, csh) . authorizedIfExists $ \(course `E.InnerJoin` sheet `E.InnerJoin` submission `E.InnerJoin` submissionUser) -> do - E.on $ submission E.^. SubmissionId E.==. submissionUser E.^. SubmissionUserSubmission - E.on $ sheet E.^. SheetId E.==. submission E.^. SubmissionSheet - E.on $ course E.^. CourseId E.==. sheet E.^. SheetCourse - E.where_ $ submissionUser E.^. SubmissionUserUser E.==. E.val participant - E.&&. course E.^. CourseTerm E.==. E.val tid - E.&&. course E.^. CourseSchool E.==. E.val ssh - E.&&. course E.^. CourseShorthand E.==. E.val csh + when (not onlyActive) $ + mapExceptT ($cachedHereBinary (participant, tid, ssh, csh)) . authorizedIfExists $ \(course `E.InnerJoin` sheet `E.InnerJoin` submission `E.InnerJoin` submissionUser) -> do + E.on $ submission E.^. SubmissionId E.==. submissionUser E.^. SubmissionUserSubmission + E.on $ sheet E.^. SheetId E.==. submission E.^. SubmissionSheet + E.on $ course E.^. CourseId E.==. sheet E.^. SheetCourse + E.where_ $ submissionUser E.^. SubmissionUserUser E.==. E.val participant + E.&&. course E.^. CourseTerm E.==. E.val tid + E.&&. course E.^. CourseSchool E.==. E.val ssh + E.&&. course E.^. CourseShorthand E.==. E.val csh -- participant is member of a submissionGroup - $cachedHereBinary (participant, tid, ssh, csh) . authorizedIfExists $ \(course `E.InnerJoin` submissionGroup `E.InnerJoin` submissionGroupUser) -> do - E.on $ submissionGroup E.^. SubmissionGroupId E.==. submissionGroupUser E.^. SubmissionGroupUserSubmissionGroup - E.on $ course E.^. CourseId E.==. submissionGroup E.^. SubmissionGroupCourse - E.where_ $ submissionGroupUser E.^. SubmissionGroupUserUser E.==. E.val participant - E.&&. course E.^. CourseTerm E.==. E.val tid - E.&&. course E.^. CourseSchool E.==. E.val ssh - E.&&. course E.^. CourseShorthand E.==. E.val csh + when (not onlyActive) $ + mapExceptT ($cachedHereBinary (participant, tid, ssh, csh)) . authorizedIfExists $ \(course `E.InnerJoin` submissionGroup `E.InnerJoin` submissionGroupUser) -> do + E.on $ submissionGroup E.^. SubmissionGroupId E.==. submissionGroupUser E.^. SubmissionGroupUserSubmissionGroup + E.on $ course E.^. CourseId E.==. submissionGroup E.^. SubmissionGroupCourse + E.where_ $ submissionGroupUser E.^. SubmissionGroupUserUser E.==. E.val participant + E.&&. course E.^. CourseTerm E.==. E.val tid + E.&&. course E.^. CourseSchool E.==. E.val ssh + E.&&. course E.^. CourseShorthand E.==. E.val csh -- participant is a sheet corrector - $cachedHereBinary (participant, tid, ssh, csh) . authorizedIfExists $ \(course `E.InnerJoin` sheet `E.InnerJoin` sheetCorrector) -> do + mapExceptT ($cachedHereBinary (participant, tid, ssh, csh)) . authorizedIfExists $ \(course `E.InnerJoin` sheet `E.InnerJoin` sheetCorrector) -> do E.on $ sheet E.^. SheetId E.==. sheetCorrector E.^. SheetCorrectorSheet E.on $ course E.^. CourseId E.==. sheet E.^. SheetCourse E.where_ $ sheetCorrector E.^. SheetCorrectorUser E.==. E.val participant @@ -1068,28 +1144,55 @@ tagAccessPredicate AuthParticipant = APDB $ \mAuthId route _ -> case route of E.&&. course E.^. CourseSchool E.==. E.val ssh E.&&. course E.^. CourseShorthand E.==. E.val csh -- participant is a tutorial user - $cachedHereBinary (participant, tid, ssh, csh) . authorizedIfExists $ \(course `E.InnerJoin` tutorial `E.InnerJoin` tutorialUser) -> do - E.on $ tutorial E.^. TutorialId E.==. tutorialUser E.^. TutorialParticipantTutorial - E.on $ course E.^. CourseId E.==. tutorial E.^. TutorialCourse - E.where_ $ tutorialUser E.^. TutorialParticipantUser E.==. E.val participant - E.&&. course E.^. CourseTerm E.==. E.val tid - E.&&. course E.^. CourseSchool E.==. E.val ssh - E.&&. course E.^. CourseShorthand E.==. E.val csh + when (not onlyActive) $ + mapExceptT ($cachedHereBinary (participant, tid, ssh, csh)) . authorizedIfExists $ \(course `E.InnerJoin` tutorial `E.InnerJoin` tutorialUser) -> do + E.on $ tutorial E.^. TutorialId E.==. tutorialUser E.^. TutorialParticipantTutorial + E.on $ course E.^. CourseId E.==. tutorial E.^. TutorialCourse + E.where_ $ tutorialUser E.^. TutorialParticipantUser E.==. E.val participant + E.&&. course E.^. CourseTerm E.==. E.val tid + E.&&. course E.^. CourseSchool E.==. E.val ssh + E.&&. course E.^. CourseShorthand E.==. E.val csh -- participant is tutor for this course - $cachedHereBinary (participant, tid, ssh, csh) . authorizedIfExists $ \(course `E.InnerJoin` tutorial `E.InnerJoin` tutor) -> do + mapExceptT ($cachedHereBinary (participant, tid, ssh, csh)) . authorizedIfExists $ \(course `E.InnerJoin` tutorial `E.InnerJoin` tutor) -> do E.on $ tutorial E.^. TutorialId E.==. tutor E.^. TutorTutorial E.on $ course E.^. CourseId E.==. tutorial E.^. TutorialCourse E.where_ $ tutor E.^. TutorUser E.==. E.val participant E.&&. course E.^. CourseTerm E.==. E.val tid E.&&. course E.^. CourseSchool E.==. E.val ssh E.&&. course E.^. CourseShorthand E.==. E.val csh + -- participant is exam corrector for this course + mapExceptT ($cachedHereBinary (participant, tid, ssh, csh)) . authorizedIfExists $ \(course `E.InnerJoin` exam `E.InnerJoin` examCorrector) -> do + E.on $ exam E.^. ExamId E.==. examCorrector E.^. ExamCorrectorExam + E.on $ course E.^. CourseId E.==. exam E.^. ExamCourse + E.where_ $ examCorrector E.^. ExamCorrectorUser E.==. E.val participant + E.&&. course E.^. CourseTerm E.==. E.val tid + E.&&. course E.^. CourseSchool E.==. E.val ssh + E.&&. course E.^. CourseShorthand E.==. E.val csh -- participant is lecturer for this course - $cachedHereBinary (participant, tid, ssh, csh) . authorizedIfExists $ \(course `E.InnerJoin` lecturer) -> do + mapExceptT ($cachedHereBinary (participant, tid, ssh, csh)) . authorizedIfExists $ \(course `E.InnerJoin` lecturer) -> do E.on $ course E.^. CourseId E.==. lecturer E.^. LecturerCourse E.where_ $ lecturer E.^. LecturerUser E.==. E.val participant E.&&. course E.^. CourseTerm E.==. E.val tid E.&&. course E.^. CourseSchool E.==. E.val ssh E.&&. course E.^. CourseShorthand E.==. E.val csh + -- participant has an exam result for this course + when (not onlyActive) $ + mapExceptT ($cachedHereBinary (participant, tid, ssh, csh)) . authorizedIfExists $ \(course `E.InnerJoin` exam `E.InnerJoin` examResult) -> do + E.on $ examResult E.^. ExamResultExam E.==. exam E.^. ExamId + E.on $ course E.^. CourseId E.==. exam E.^. ExamCourse + E.where_ $ examResult E.^. ExamResultUser E.==. E.val participant + E.&&. course E.^. CourseTerm E.==. E.val tid + E.&&. course E.^. CourseSchool E.==. E.val ssh + E.&&. course E.^. CourseShorthand E.==. E.val csh + -- participant is registered for an exam for this course + when (not onlyActive) $ + mapExceptT ($cachedHereBinary (participant, tid, ssh, csh)) . authorizedIfExists $ \(course `E.InnerJoin` exam `E.InnerJoin` examRegistration) -> do + E.on $ examRegistration E.^. ExamRegistrationExam E.==. exam E.^. ExamId + E.on $ course E.^. CourseId E.==. exam E.^. ExamCourse + E.where_ $ examRegistration E.^. ExamRegistrationUser E.==. E.val participant + E.&&. course E.^. CourseTerm E.==. E.val tid + E.&&. course E.^. CourseSchool E.==. E.val ssh + E.&&. course E.^. CourseShorthand E.==. E.val csh return () tagAccessPredicate AuthApplicant = APDB $ \mAuthId route _ -> case route of @@ -1129,7 +1232,7 @@ tagAccessPredicate AuthCapacity = APDB $ \_ route _ -> case route of return Authorized CourseR tid ssh csh _ -> maybeT (unauthorizedI MsgCourseNoCapacity) $ do Entity cid Course{..} <- $cachedHereBinary (tid, ssh, csh) . MaybeT . getBy $ TermSchoolCourseShort tid ssh csh - registered <- $cachedHereBinary cid . lift $ count [ CourseParticipantCourse ==. cid ] + registered <- $cachedHereBinary cid . lift $ count [ CourseParticipantCourse ==. cid, CourseParticipantState ==. CourseParticipantActive ] guard $ NTop courseCapacity > NTop (Just registered) return Authorized r -> $unsupportedAuthPredicate AuthCapacity r @@ -1256,6 +1359,12 @@ tagAccessPredicate AuthAuthentication = APDB $ \mAuthId route _ -> case route of let isAuthenticated = isJust mAuthId guard $ not systemMessageAuthenticatedOnly || isAuthenticated return Authorized + MessageHideR cID -> maybeT (unauthorizedI MsgUnauthorizedSystemMessageAuth) $ do + smId <- catchIfMaybeT (const True :: CryptoIDError -> Bool) $ decrypt cID + SystemMessage{..} <- $cachedHereBinary smId . MaybeT $ get smId + let isAuthenticated = isJust mAuthId + guard $ not systemMessageAuthenticatedOnly || isAuthenticated + return Authorized r -> $unsupportedAuthPredicate AuthAuthentication r tagAccessPredicate AuthRead = APHandler . const . const $ bool (return Authorized) (unauthorizedI MsgUnauthorizedWrite) tagAccessPredicate AuthWrite = APHandler . const . const $ bool (unauthorizedI MsgUnauthorized) (return Authorized) @@ -1413,47 +1522,17 @@ data instance ButtonClass UniWorX | BCLink | BCMassInputAdd | BCMassInputDelete deriving (Enum, Eq, Ord, Bounded, Read, Show, Generic, Typeable) -instance Universe (ButtonClass UniWorX) -instance Finite (ButtonClass UniWorX) + deriving anyclass (Universe, Finite) instance PathPiece (ButtonClass UniWorX) where toPathPiece BCIsButton = "btn" toPathPiece bClass = ("btn-" <>) . camelToPathPiece' 1 $ tshow bClass - fromPathPiece = finiteFromPathPiece + fromPathPiece = flip List.lookup $ map (toPathPiece &&& id) universeF -embedRenderMessage ''UniWorX ''ButtonSubmit id instance Button UniWorX ButtonSubmit where btnClasses BtnSubmit = [BCIsButton, BCPrimary] -updateFavourites :: forall m. (MonadHandler m, HandlerSite m ~ UniWorX) - => Maybe (TermId, SchoolId, CourseShorthand) -- ^ Insert course into favourites, as appropriate - -> ReaderT SqlBackend m () -updateFavourites cData = void . runMaybeT $ do - $logDebugS "updateFavourites" "Updating favourites" - - now <- liftIO $ getCurrentTime - uid <- MaybeT $ liftHandler maybeAuthId - mcid <- for cData $ \(tid, ssh, csh) -> MaybeT . getKeyBy $ TermSchoolCourseShort tid ssh csh - User{userMaxFavourites} <- MaybeT $ get uid - - -- update Favourites - for_ mcid $ \cid -> - void . lift $ upsertBy - (UniqueCourseFavourite uid cid) - (CourseFavourite uid cid FavouriteVisited now) - [CourseFavouriteLastVisit =. now] - -- prune Favourites to user-defined size - oldFavs <- lift $ selectList [CourseFavouriteUser ==. uid] [] - let deleteFavs = oldFavs - & sortOn ((courseFavouriteReason &&& Down . courseFavouriteLastVisit) . entityVal) - & drop userMaxFavourites - & filter ((<= FavouriteVisited) . courseFavouriteReason . entityVal) - & map entityKey - unless (null deleteFavs) $ - lift $ deleteWhere [CourseFavouriteId <-. deleteFavs] - - -- Please see the documentation for the Yesod typeclass. There are a number -- of settings which can be configured by overriding methods here. @@ -1465,11 +1544,49 @@ instance Yesod UniWorX where Nothing -> getApprootText guessApproot app req Just root -> root - -- Store session data on the client in encrypted cookies, - -- default session idle timeout is 120 minutes - makeSessionBackend app = do - (getCachedDate, _) <- clientSessionDateCacher (app ^. _appSessionTimeout) - return . Just $ clientSessionBackend (app ^. _appSessionKey) getCachedDate + makeSessionBackend app@UniWorX{ appSettings' = AppSettings{..}, ..} = notForBearer . sameSite $ case appSessionStore of + SessionStorageMemcachedSql sqlStore + -> mkBackend =<< stateSettings <$> ServerSession.createState sqlStore + SessionStorageAcid acidStore + | appServerSessionAcidFallback + -> mkBackend =<< stateSettings <$> ServerSession.createState acidStore + _other + -> return Nothing + where + cfg = JwtSession.ServerSessionJwtConfig + { sJwtJwkSet = appJSONWebKeySet + , sJwtStart = Nothing + , sJwtExpiration = appSessionTokenExpiration + , sJwtEncoding = appSessionTokenEncoding + , sJwtIssueBy = appInstanceID + , sJwtIssueFor = appClusterID + } + mkBackend :: forall sto. + ( ServerSession.SessionData sto ~ Map Text ByteString + , ServerSession.Storage sto + ) + => ServerSession.State sto -> IO (Maybe SessionBackend) + mkBackend = JwtSession.backend cfg (JwtSession.siteApproot app) + stateSettings :: forall sto. ServerSession.State sto -> ServerSession.State sto + stateSettings = ServerSession.setCookieName (toPathPiece CookieSession) . applyServerSessionSettings appServerSessionConfig + sameSite + | Just sameSiteStrict == cookieSameSite (getCookieSettings app CookieSession) + = strictSameSiteSessions + | Just sameSiteLax == cookieSameSite (getCookieSettings app CookieSession) + = laxSameSiteSessions + | otherwise + = id + notForBearer :: IO (Maybe SessionBackend) -> IO (Maybe SessionBackend) + notForBearer = fmap $ fmap notForBearer' + where notForBearer' :: SessionBackend -> SessionBackend + notForBearer' (SessionBackend load) + = let load' req + | aHdrs <- mapMaybe (\(h, v) -> v <$ guard (h == W.hAuthorization)) $ W.requestHeaders req + , any (is _Just) $ map W.extractBearerAuth aHdrs + = return (mempty, const $ return []) + | otherwise + = load req + in SessionBackend load' maximumContentLength app _ = app ^. _appMaximumContentLength @@ -1480,7 +1597,7 @@ instance Yesod UniWorX where -- b) Validates that incoming write requests include that token in either a header or POST parameter. -- To add it, chain it together with the defaultMiddleware: yesodMiddleware = defaultYesodMiddleware . defaultCsrfMiddleware -- For details, see the CSRF documentation in the Yesod.Core.Handler module of the yesod-core package. - yesodMiddleware = languagesMiddleware appLanguages . headerMessagesMiddleware . defaultYesodMiddleware . normalizeRouteMiddleware . defaultCsrfMiddleware . updateFavouritesMiddleware + yesodMiddleware = observeYesodCacheSizeMiddleware . languagesMiddleware appLanguages . headerMessagesMiddleware . defaultYesodMiddleware . normalizeRouteMiddleware . csrfMiddleware . updateFavouritesMiddleware . storeBearerMiddleware where updateFavouritesMiddleware :: Handler a -> Handler a updateFavouritesMiddleware handler = (*> handler) . runMaybeT $ do @@ -1512,48 +1629,95 @@ instance Yesod UniWorX where lift . bracketOnError getMessages (mapM_ addMessage') $ addCustomHeader HeaderAlerts . decodeUtf8 . urlEncode True . toStrict . JSON.encode + observeYesodCacheSizeMiddleware :: Handler a -> Handler a + observeYesodCacheSizeMiddleware handler = handler `finally` observeYesodCacheSize + csrfMiddleware :: Handler a -> Handler a + csrfMiddleware handler = do + hasBearer <- is _Just <$> lookupBearerAuth + + if | hasBearer -> handler + | otherwise -> csrfSetCookieMiddleware' . defaultCsrfCheckMiddleware $ handler + where + csrfSetCookieMiddleware' handler' = do + mcsrf <- reqToken <$> getRequest + whenIsJust mcsrf $ setRegisteredCookie CookieXSRFToken + handler' + storeBearerMiddleware :: Handler a -> Handler a + storeBearerMiddleware handler = do + askBearer >>= \case + Just (Jwt bs) -> setSessionBS (toPathPiece SessionBearer) bs + Nothing -> return () + + handler -- Since we implement `errorHandler` ourselves we don't need `defaultMessageWidget` defaultMessageWidget _title _body = error "defaultMessageWidget: undefined" errorHandler err = do - mr <- getMessageRender - let - encrypted :: ToJSON a => a -> Widget -> Widget - encrypted plaintextJson plaintext = do - canDecrypt <- (== Authorized) <$> evalAccess AdminErrMsgR True - shouldEncrypt <- getsYesod $ view _appEncryptErrors - if - | shouldEncrypt - , not canDecrypt -> do - ciphertext <- encodedSecretBox SecretBoxPretty plaintextJson + shouldEncrypt <- do + canDecrypt <- (== Authorized) <$> evalAccess AdminErrMsgR True + shouldEncrypt <- getsYesod $ view _appEncryptErrors + return $ shouldEncrypt && not canDecrypt - [whamlet| -
_{MsgErrorResponseEncrypted} -
- #{ciphertext}
- |]
- | otherwise -> plaintext
+ sessErr <- bool return (_InternalError $ encodedSecretBox SecretBoxShort) shouldEncrypt err
+ setSessionJson SessionError sessErr
- errPage = case err of
- NotFound -> [whamlet|_{MsgErrorResponseNotFound}|]
- InternalError err' -> encrypted err' [whamlet|
#{err'}|]
- InvalidArgs errs -> [whamlet|
-
_{MsgErrorResponseNotAuthenticated}|] - PermissionDenied err' -> [whamlet|
#{err'}|] - BadMethod method -> [whamlet|
_{MsgErrorResponseBadMethod (decodeUtf8 method)}|] - fmap toTypedContent . siteLayout (toWgt . mr $ ErrorResponseTitle err) $ do - toWidget - [cassius| - .errMsg - white-space: pre-wrap - font-family: monospace - |] - errPage + selectRep $ do + provideRep $ do + mr <- getMessageRender + let + encrypted :: ToJSON a => a -> Widget -> Widget + encrypted plaintextJson plaintext = do + if + | shouldEncrypt -> do + ciphertext <- encodedSecretBox SecretBoxPretty plaintextJson + + [whamlet| +
_{MsgErrorResponseEncrypted} +
+ #{ciphertext}
+ |]
+ | otherwise -> plaintext
+
+ errPage = case err of
+ NotFound -> [whamlet|_{MsgErrorResponseNotFound}|]
+ InternalError err' -> encrypted err' [whamlet|
#{err'}|]
+ InvalidArgs errs -> [whamlet|
+
_{MsgErrorResponseNotAuthenticated}|] + PermissionDenied err' -> [whamlet|
#{err'}|] + BadMethod method -> [whamlet|
_{MsgErrorResponseBadMethod (decodeUtf8 method)}|] + siteLayout (toWgt . mr $ ErrorResponseTitle err) $ do + toWidget + [cassius| + .errMsg + white-space: pre-wrap + font-family: monospace + |] + errPage + provideRep . fmap PrettyValue $ case err of + PermissionDenied err' -> return $ object [ "message" JSON..= err' ] + InternalError err' + | shouldEncrypt -> do + ciphertext <- encodedSecretBox SecretBoxShort err' + return $ object [ "message" JSON..= ciphertext + , "encrypted" JSON..= True + ] + | otherwise -> return $ object [ "message" JSON..= err' ] + InvalidArgs errs -> return $ object [ "messages" JSON..= errs ] + _other -> return $ object [] + provideRep $ case err of + PermissionDenied err' -> return err' + InternalError err' + | shouldEncrypt -> do + addHeader "Encrypted-Error-Message" "True" + encodedSecretBox SecretBoxPretty err' + | otherwise -> return err' + InvalidArgs errs -> return . Text.unlines . map (Text.replace "\n" "\n\t") $ errs + _other -> return Text.empty defaultLayout = siteLayout' Nothing @@ -1564,8 +1728,8 @@ instance Yesod UniWorX where addStaticContent ext _mime content = do UniWorX{appWidgetMemcached, appSettings'} <- getYesod - for ((,) <$> appWidgetMemcached <*> appWidgetMemcachedConf appSettings') $ \(mConn, WidgetMemcachedConf{ widgetMemcachedConnectInfo = _, .. }) -> do - let expiry = (maybe 0 ceiling widgetMemcachedExpiry) + for ((,) <$> appWidgetMemcached <*> appWidgetMemcachedConf appSettings') $ \(mConn, WidgetMemcachedConf{ widgetMemcachedConf = MemcachedConf { memcachedExpiry }, widgetMemcachedBaseUrl }) -> do + let expiry = maybe 0 ceiling memcachedExpiry touch = liftIO $ Memcached.touch expiry (encodeUtf8 $ pack fileName) mConn add = liftIO $ Memcached.add zeroBits expiry (encodeUtf8 $ pack fileName) content mConn absoluteLink = unpack widgetMemcachedBaseUrl > fileName @@ -1610,6 +1774,45 @@ instance Yesod UniWorX where -- (Just lang) -- return ((,) <$> langBoxRes <*> urlRes, toWidget csrf <> fvInput urlView <> fvInput langBoxView) +data MemcachedKeyFavourites + = MemcachedKeyFavouriteQuickActions CourseId AuthContext (NonEmpty Lang) + deriving (Eq, Read, Show, Generic, Typeable) + deriving anyclass (Hashable, Binary) + +data MemcachedLimitKeyFavourites + = MemcachedLimitKeyFavourites + deriving (Eq, Ord, Read, Show, Generic, Typeable) + deriving anyclass (Hashable, Binary) + + +updateFavourites :: forall m. (MonadHandler m, HandlerSite m ~ UniWorX) + => Maybe (TermId, SchoolId, CourseShorthand) -- ^ Insert course into favourites, as appropriate + -> ReaderT SqlBackend m () +updateFavourites cData = void . runMaybeT $ do + $logDebugS "updateFavourites" "Updating favourites" + + now <- liftIO $ getCurrentTime + uid <- MaybeT $ liftHandler maybeAuthId + mcid <- for cData $ \(tid, ssh, csh) -> MaybeT . getKeyBy $ TermSchoolCourseShort tid ssh csh + User{userMaxFavourites} <- MaybeT $ get uid + + -- update Favourites + for_ mcid $ \cid -> + void . lift $ upsertBy + (UniqueCourseFavourite uid cid) + (CourseFavourite uid cid FavouriteVisited now) + [CourseFavouriteLastVisit =. now] + -- prune Favourites to user-defined size + oldFavs <- lift $ selectList [CourseFavouriteUser ==. uid] [] + let deleteFavs = oldFavs + & sortOn ((courseFavouriteReason &&& Down . courseFavouriteLastVisit) . entityVal) + & drop userMaxFavourites + & filter ((<= FavouriteVisited) . courseFavouriteReason . entityVal) + & map entityKey + unless (null deleteFavs) $ + lift $ deleteWhere [CourseFavouriteId <-. deleteFavs] + + siteLayoutMsg :: (RenderMessage site msg, site ~ UniWorX) => msg -> Widget -> Handler Html siteLayoutMsg msg widget = do mr <- getMessageRender @@ -1634,6 +1837,8 @@ siteLayout' headingOverride widget = do mcurrentRoute <- getCurrentRoute let currentHandler = classifyHandler <$> mcurrentRoute + currentApproot' <- siteApproot <$> getYesod <*> (reqWaiRequest <$> getRequest) + -- Get the breadcrumbs, as defined in the YesodBreadcrumbs instance. let breadcrumbs' mcRoute = do @@ -1679,6 +1884,7 @@ siteLayout' headingOverride widget = do isParticipant = E.exists . E.from $ \participant -> E.where_ $ participant E.^. CourseParticipantCourse E.==. course E.^. CourseId E.&&. E.just (participant E.^. CourseParticipantUser) E.==. E.val (view _1 <$> muid) + E.&&. participant E.^. CourseParticipantState E.==. E.val CourseParticipantActive isLecturer = E.exists . E.from $ \lecturer -> E.where_ $ lecturer E.^. LecturerCourse E.==. course E.^. CourseId E.&&. E.just (lecturer E.^. LecturerUser) E.==. E.val (view _1 <$> muid) @@ -1705,13 +1911,33 @@ siteLayout' headingOverride widget = do , maybe userDefaultMaxFavouriteTerms userMaxFavouriteTerms $ view _2 <$> muid , maybe userDefaultTheme userTheme $ view _2 <$> muid ) - favourites <- forM favourites' $ \(Entity _ c@Course{..}, E.Value mFavourite) + + let favouriteTerms :: [TermIdentifier] + favouriteTerms = take maxFavouriteTerms . Set.toDescList $ foldMap (\(Entity _ Course{..}, _) -> Set.singleton $ unTermKey courseTerm) favourites' + + favourites <- fmap catMaybes . forM favourites' $ \(Entity cId c@Course{..}, E.Value mFavourite) -> let courseRoute = CourseR courseTerm courseSchool courseShorthand CShowR favouriteReason = fromMaybe FavouriteCurrent mFavourite - in do - items' <- pageQuickActions NavQuickViewFavourite courseRoute - items <- forM items' $ \n -> (n,) <$> toTextUrl n - return (c, courseRoute, items, favouriteReason) + in runMaybeT . guardOnM (unTermKey courseTerm `elem` favouriteTerms) . lift $ do + ctx <- getAuthContext + MsgRenderer mr <- getMsgRenderer + langs <- selectLanguages appLanguages <$> languages + let cK = MemcachedKeyFavouriteQuickActions cId ctx langs + $logDebugS "FavouriteQuickActions" $ tshow cK <> " Checking..." + items <- memcachedLimitedKeyTimeoutBy + MemcachedLimitKeyFavourites appFavouritesQuickActionsBurstsize appFavouritesQuickActionsAvgInverseRate 1 + (Right <$> appFavouritesQuickActionsCacheTTL) + appFavouritesQuickActionsTimeout + cK + cK + . observeFavouritesQuickActionsDuration $ do + $logDebugS "FavouriteQuickActions" $ tshow cK <> " Starting..." + items' <- pageQuickActions NavQuickViewFavourite courseRoute + items <- forM items' $ \n@NavLink{navLabel} -> (mr navLabel,) <$> toTextUrl n + $logDebugS "FavouriteQuickActions" $ tshow cK <> " Done." + return items + $logDebugS "FavouriteQuickActions" $ tshow cK <> " returning " <> tshow (is _Just items) + return (c, courseRoute, items, favouriteReason) nav'' <- mconcat <$> sequence [ defaultLinks @@ -1721,7 +1947,7 @@ siteLayout' headingOverride widget = do nav <- forM nav' $ \n -> (n,,,) <$> newIdent <*> traverse toTextUrl (n ^? _navLink) <*> traverse (\nc -> (nc,, ) <$> newIdent <*> toTextUrl nc) (n ^. _navChildren) mmsgs <- if - | isModal -> getMessages + | isModal -> return mempty | otherwise -> do applySystemMessages authTagPivots <- fromMaybe Set.empty <$> takeSessionJson SessionInactiveAuthTags @@ -1743,9 +1969,7 @@ siteLayout' headingOverride widget = do navItems = map (view _2) favourites ++ toListOf (folded . typesUsing @NavChildren @NavLink . to urlRoute) nav highR = find (`elem` navItems) . uncurry (++) $ partition (`elem` map (view _2) favourites) crumbs highlightNav = (||) <$> navForceActive <*> highlight - favouriteTerms :: [TermIdentifier] - favouriteTerms = take maxFavouriteTerms . Set.toDescList $ foldMap (\(Course{..}, _, _, _) -> Set.singleton $ unTermKey courseTerm) favourites - favouriteTermReason :: TermIdentifier -> FavouriteReason -> [(Course, Route UniWorX, [(NavLink, Text)], FavouriteReason)] + favouriteTermReason :: TermIdentifier -> FavouriteReason -> [(Course, Route UniWorX, Maybe [(Text, Text)], FavouriteReason)] favouriteTermReason tid favReason' = favourites & filter (\(Course{..}, _, _, favReason) -> unTermKey courseTerm == tid && favReason == favReason') & sortOn (\(Course{..}, _, _, _) -> courseName) @@ -1881,16 +2105,68 @@ siteLayout' headingOverride widget = do pc <- widgetToPageContent $ do webpackLinks_main StaticR toWidget $(juliusFile "templates/i18n.julius") + whenIsJust currentApproot' $ \currentApproot -> + toWidget $(juliusFile "templates/approot.julius") + whenIsJust mcurrentRoute $ \currentRoute' -> do + currentRoute <- toTextUrl currentRoute' + toWidget $(juliusFile "templates/current-route.julius") wellKnownHtmlLinks $(widgetFile "default-layout") withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet") -applySystemMessages :: (MonadHandler m, HandlerSite m ~ UniWorX) => m () -applySystemMessages = liftHandler . runDB . runConduit $ selectSource [] [] .| C.mapM_ applyMessage +getSystemMessageState :: (MonadHandler m, HandlerSite m ~ UniWorX) => SystemMessageId -> m UserSystemMessageState +getSystemMessageState smId = liftHandler $ do + muid <- maybeAuthId + reqSt <- $cachedHere getSystemMessageStateRequest + dbSt <- $cachedHere $ maybe (return mempty) getDBSystemMessageState muid + let MergeHashMap smSt = reqSt <> dbSt + smSt' = MergeHashMap $ HashMap.filter (/= mempty) smSt + when (smSt' /= reqSt) $ + setRegisteredCookieJson CookieSystemMessageState + =<< ifoldMapM (\smId' v -> MergeHashMap <$> (HashMap.singleton <$> encrypt smId' <*> pure v :: Handler (HashMap CryptoUUIDSystemMessage _))) smSt' + + return . fromMaybe mempty $ HashMap.lookup smId smSt where + getSystemMessageStateRequest = + (lookupRegisteredCookiesJson id CookieSystemMessageState :: Handler (MergeHashMap CryptoUUIDSystemMessage UserSystemMessageState)) + >>= ifoldMapM (\(cID :: CryptoUUIDSystemMessage) v -> MergeHashMap <$> (maybeT (return mempty) . catchMPlus (Proxy @CryptoIDError) $ HashMap.singleton <$> decrypt cID <*> pure v)) + getDBSystemMessageState uid = runDB . runConduit $ selectSource [ SystemMessageHiddenUser ==. uid ] [] .| C.foldMap foldSt + where foldSt (Entity _ SystemMessageHidden{..}) + = MergeHashMap . HashMap.singleton systemMessageHiddenMessage $ mempty { userSystemMessageHidden = Just systemMessageHiddenTime } + +applySystemMessages :: (MonadHandler m, HandlerSite m ~ UniWorX) => m () +applySystemMessages = liftHandler . maybeT_ . catchMPlus (Proxy @CryptoIDError) $ do + lift $ maybeAuthId >>= traverse_ syncSystemMessageHidden + + cRoute <- lift getCurrentRoute + guard $ cRoute /= Just NewsR + + lift . runDB . runConduit $ selectSource [] [] .| C.mapM_ applyMessage + where + syncSystemMessageHidden uid = runDB $ do + smSt <- lookupRegisteredCookiesJson id CookieSystemMessageState :: DB (MergeHashMap CryptoUUIDSystemMessage UserSystemMessageState) + iforM_ smSt $ \cID UserSystemMessageState{..} -> do + smId <- decrypt cID + whenIsJust userSystemMessageHidden $ \systemMessageHiddenTime -> void $ + upsert SystemMessageHidden + { systemMessageHiddenMessage = smId + , systemMessageHiddenUser = uid + , systemMessageHiddenTime + } + [ SystemMessageHiddenTime =. systemMessageHiddenTime ] + + when (maybe False (maybe (const True) (<=) userSystemMessageHidden) userSystemMessageUnhidden) $ do + deleteBy $ UniqueSystemMessageHidden uid smId + + modifyRegisteredCookieJson CookieSystemMessageState $ \(fold -> MergeHashMap hm) + -> fmap MergeHashMap . assertM' (/= mempty) $ + HashMap.update (\smSt' -> assertM' (/= mempty) $ smSt' { userSystemMessageHidden = Nothing, userSystemMessageUnhidden = Nothing }) cID hm + applyMessage (Entity smId SystemMessage{..}) = maybeT_ $ do + guard $ not systemMessageNewsOnly + cID <- encrypt smId void . assertM (== Authorized) . lift $ evalAccessDB (MessageR cID) False @@ -1898,9 +2174,9 @@ applySystemMessages = liftHandler . runDB . runConduit $ selectSource [] [] .| C guard $ NTop systemMessageFrom <= NTop (Just now) guard $ NTop (Just now) < NTop systemMessageTo - let sessionKey = "sm-" <> tshow (ciphertext cID) - _ <- assertM isNothing $ lookupSessionJson sessionKey :: MaybeT (YesodDB UniWorX) (Maybe ()) - setSessionJson sessionKey () + UserSystemMessageState{..} <- lift $ getSystemMessageState smId + guard $ userSystemMessageShown <= Just systemMessageLastChanged + guard $ userSystemMessageHidden <= Just systemMessageLastUnhide (_, smTrans) <- MaybeT $ getSystemMessage appLanguages smId let @@ -1912,6 +2188,9 @@ applySystemMessages = liftHandler . runDB . runConduit $ selectSource [] [] .| C addMessageWidget systemMessageSeverity $ msgModal (toWidget s) (Left . SomeRoute $ MessageR cID) Nothing -> addMessage systemMessageSeverity content + tellRegisteredCookieJson CookieSystemMessageState . MergeHashMap $ + HashMap.singleton cID mempty{ userSystemMessageShown = Just now } + -- Define breadcrumbs. i18nCrumb :: ( RenderMessage (HandlerSite m) msg, MonadHandler m ) => msg @@ -1966,6 +2245,7 @@ instance YesodBreadcrumbs UniWorX where breadcrumb AdminFeaturesR = i18nCrumb MsgAdminFeaturesHeading $ Just AdminR breadcrumb AdminTestR = i18nCrumb MsgMenuAdminTest $ Just AdminR breadcrumb AdminErrMsgR = i18nCrumb MsgMenuAdminErrMsg $ Just AdminR + breadcrumb AdminTokensR = i18nCrumb MsgMenuAdminTokens $ Just AdminR breadcrumb SchoolListR = i18nCrumb MsgMenuSchoolList $ Just AdminR breadcrumb (SchoolR ssh SchoolEditR) = maybeT (i18nCrumb MsgBreadcrumbSchool $ Just SchoolListR) $ do @@ -1983,6 +2263,7 @@ instance YesodBreadcrumbs UniWorX where breadcrumb LegalR = i18nCrumb MsgMenuLegal $ Just InfoR breadcrumb InfoAllocationR = i18nCrumb MsgBreadcrumbAllocationInfo $ Just InfoR breadcrumb VersionR = i18nCrumb MsgMenuVersion $ Just InfoR + breadcrumb FaqR = i18nCrumb MsgBreadcrumbFaq $ Just InfoR breadcrumb HelpR = i18nCrumb MsgMenuHelp Nothing @@ -2030,6 +2311,8 @@ instance YesodBreadcrumbs UniWorX where return (CI.original courseName, Just $ AllocationR tid ssh ash AShowR) AUsersR -> i18nCrumb MsgBreadcrumbAllocationUsers . Just $ AllocationR tid ssh ash AShowR APriosR -> i18nCrumb MsgBreadcrumbAllocationPriorities . Just $ AllocationR tid ssh ash AUsersR + AComputeR -> i18nCrumb MsgBreadcrumbAllocationCompute . Just $ AllocationR tid ssh ash AUsersR + AAcceptR -> i18nCrumb MsgBreadcrumbAllocationAccept . Just $ AllocationR tid ssh ash AUsersR breadcrumb ParticipantsListR = i18nCrumb MsgBreadcrumbParticipantsList $ Just CourseListR breadcrumb (ParticipantsR _ _) = i18nCrumb MsgBreadcrumbParticipants $ Just ParticipantsListR @@ -2175,6 +2458,7 @@ instance YesodBreadcrumbs UniWorX where | mayList -> i18nCrumb MsgBreadcrumbSystemMessage $ Just MessageListR | otherwise -> i18nCrumb MsgBreadcrumbSystemMessage $ Just NewsR breadcrumb MessageListR = i18nCrumb MsgMenuMessageList $ Just AdminR + breadcrumb (MessageHideR cID) = i18nCrumb MsgBreadcrumbMessageHide . Just $ MessageR cID breadcrumb GlossaryR = i18nCrumb MsgMenuGlossary $ Just InfoR @@ -2332,6 +2616,14 @@ defaultLinks = fmap catMaybes . mapM runMaybeT $ -- Define the menu items of the , navQuick' = mempty , navForceActive = False } + , return $ NavFooter NavLink + { navLabel = MsgMenuFaq + , navRoute = FaqR + , navAccess' = return True + , navType = NavTypeLink { navModal = False } + , navQuick' = mempty + , navForceActive = False + } , return $ NavFooter NavLink { navLabel = MsgMenuGlossary , navRoute = GlossaryR @@ -2433,6 +2725,14 @@ defaultLinks = fmap catMaybes . mapM runMaybeT $ -- Define the menu items of the , navQuick' = mempty , navForceActive = False } + , NavLink + { navLabel = MsgMenuAdminTokens + , navRoute = AdminTokensR + , navAccess' = return True + , navType = NavTypeLink { navModal = False } + , navQuick' = mempty + , navForceActive = False + } , NavLink { navLabel = MsgMenuAdminTest , navRoute = AdminTestR @@ -2563,6 +2863,7 @@ pageActions (CourseR tid ssh csh CShowR) = do E.&&. course E.^. CourseShorthand E.==. E.val csh hasParticipants = E.selectExists . E.from $ \(course `E.InnerJoin` courseParticipant) -> do E.on $ course E.^. CourseId E.==. courseParticipant E.^. CourseParticipantCourse + E.where_ $ courseParticipant E.^. CourseParticipantState E.==. E.val CourseParticipantActive void $ courseWhere course mayRegister = hasWriteAccessTo $ CourseR tid ssh csh CAddUserR in runDB $ mayRegister `or2M` hasParticipants @@ -2825,6 +3126,17 @@ pageActions InfoR = return } , navChildren = [] } + , NavPageActionPrimary + { navLink = NavLink + { navLabel = MsgMenuFaq + , navRoute = FaqR + , navAccess' = return True + , navType = NavTypeLink { navModal = False } + , navQuick' = mempty + , navForceActive = False + } + , navChildren = [] + } , NavPageActionPrimary { navLink = NavLink { navLabel = MsgMenuGlossary @@ -2860,6 +3172,17 @@ pageActions VersionR = return } , navChildren = [] } + , NavPageActionPrimary + { navLink = NavLink + { navLabel = MsgMenuFaq + , navRoute = FaqR + , navAccess' = return True + , navType = NavTypeLink { navModal = False } + , navQuick' = mempty + , navForceActive = False + } + , navChildren = [] + } , NavPageActionPrimary { navLink = NavLink { navLabel = MsgMenuGlossary @@ -2900,6 +3223,17 @@ pageActions InstanceR = return ] pageActions HelpR = return [ NavPageActionPrimary + { navLink = NavLink + { navLabel = MsgMenuFaq + , navRoute = FaqR + , navAccess' = return True + , navType = NavTypeLink { navModal = False } + , navQuick' = mempty + , navForceActive = False + } + , navChildren = [] + } + , NavPageActionPrimary { navLink = NavLink { navLabel = MsgInfoLecturerTitle , navRoute = InfoLecturerR @@ -3021,6 +3355,17 @@ pageActions (AllocationR tid ssh ash AShowR) = return } , navChildren = [] } + , NavPageActionPrimary + { navLink = NavLink + { navLabel = MsgMenuAllocationCompute + , navRoute = AllocationR tid ssh ash AComputeR + , navAccess' = return True + , navType = NavTypeLink { navModal = False } + , navQuick' = mempty + , navForceActive = False + } + , navChildren = [] + } ] pageActions (AllocationR tid ssh ash AUsersR) = return [ NavPageActionPrimary @@ -3034,6 +3379,17 @@ pageActions (AllocationR tid ssh ash AUsersR) = return } , navChildren = [] } + , NavPageActionPrimary + { navLink = NavLink + { navLabel = MsgMenuAllocationCompute + , navRoute = AllocationR tid ssh ash AComputeR + , navAccess' = return True + , navType = NavTypeLink { navModal = False } + , navQuick' = mempty + , navForceActive = False + } + , navChildren = [] + } ] pageActions CourseListR = do participantsSecondary <- pageQuickActions NavQuickViewPageActionSecondary ParticipantsListR @@ -3819,6 +4175,17 @@ pageActions (EExamR tid ssh coursen examn EEGradesR) = return } , navChildren = [] } + , NavPageActionPrimary + { navLink = NavLink + { navLabel = MsgMenuExternalExamEdit + , navRoute = EExamR tid ssh coursen examn EEEditR + , navAccess' = return True + , navType = NavTypeLink { navModal = False } + , navQuick' = mempty + , navForceActive = False + } + , navChildren = [] + } ] pageActions (EExamR tid ssh coursen examn EEUsersR) = return [ NavPageActionPrimary @@ -3832,6 +4199,17 @@ pageActions (EExamR tid ssh coursen examn EEUsersR) = return } , navChildren = [] } + , NavPageActionPrimary + { navLink = NavLink + { navLabel = MsgMenuExternalExamEdit + , navRoute = EExamR tid ssh coursen examn EEEditR + , navAccess' = return True + , navType = NavTypeLink { navModal = False } + , navQuick' = mempty + , navForceActive = False + } + , navChildren = [] + } ] pageActions ParticipantsListR = return [ NavPageActionPrimary @@ -4112,17 +4490,7 @@ data CampusUserConversionException | CampusUserInvalidFeaturesOfStudy Text | CampusUserInvalidAssociatedSchools Text deriving (Eq, Ord, Read, Show, Generic, Typeable) -instance Exception CampusUserConversionException - -embedRenderMessage ''UniWorX ''CampusUserConversionException id - -data UpsertCampusUserMode - = UpsertCampusUser - | UpsertCampusUserDummy { upsertCampusUserIdent :: UserIdent } - | UpsertCampusUserOther { uspertCampusUserIdent :: UserIdent } - deriving (Eq, Ord, Read, Show, Generic, Typeable) -makeLenses_ ''UpsertCampusUserMode -makePrisms ''UpsertCampusUserMode + deriving anyclass (Exception) _upsertCampusUserMode :: Traversal' (Creds UniWorX) UpsertCampusUserMode _upsertCampusUserMode mMode cs@Creds{..} @@ -4387,18 +4755,11 @@ upsertCampusUser plugin ldapData = do oldFs <- selectKeysList ([ StudyFeaturesUser ==. studyFeaturesUser , StudyFeaturesDegree ==. studyFeaturesDegree + , StudyFeaturesField ==. studyFeaturesField , StudyFeaturesType ==. studyFeaturesType , StudyFeaturesSemester ==. studyFeaturesSemester - ] ++ - [ StudyFeaturesField ==. studyFeaturesField - , StudyFeaturesSuperField ==. studyFeaturesSuperField - ] ||. case studyFeaturesSuperField of - Just sField -> - [ StudyFeaturesField ==. sField - , StudyFeaturesSuperField ==. Nothing - ] - Nothing -> [] - ) [] + ]) + [] case oldFs of [oldF] -> update oldF [ StudyFeaturesUpdated =. now @@ -4459,16 +4820,6 @@ associateUserSchoolsByTerms uid = do , userSchoolIsOptOut = False } -setLangCookie :: MonadHandler m => Lang -> m () -setLangCookie lang = do - now <- liftIO getCurrentTime - setCookie $ def - { setCookieName = "_LANG" - , setCookieValue = encodeUtf8 lang - , setCookieExpires = Just $ addUTCTime (400 * avgNominalYear) now - , setCookiePath = Just "/" - } - updateUserLanguage :: Maybe Lang -> DB (Maybe Lang) updateUserLanguage (Just lang) = do unless (lang `elem` appLanguages) $ @@ -4478,7 +4829,7 @@ updateUserLanguage (Just lang) = do for_ muid $ \uid -> do langs <- languages update uid [ UserLanguages =. Just (Languages $ lang : nub (filter ((&&) <$> (`elem` appLanguages) <*> (/= lang)) langs)) ] - setLangCookie lang + setRegisteredCookie CookieLang lang return $ Just lang updateUserLanguage Nothing = runMaybeT $ do uid <- MaybeT maybeAuthId @@ -4497,7 +4848,7 @@ updateUserLanguage Nothing = runMaybeT $ do -> return l (_, [], _) -> mzero - setLangCookie lang + setRegisteredCookie CookieLang lang return lang @@ -4574,17 +4925,17 @@ instance YesodAuth UniWorX where $logDebugS "auth" $ tshow Creds{..} UniWorX{ appSettings' = AppSettings{ appUserDefaults = UserDefaultConf{..}, ..}, .. } <- getYesod - flip catches excHandlers $ case (,) <$> appLdapConf <*> appLdapPool of - Just (ldapConf, ldapPool) + flip catches excHandlers $ case appLdapPool of + Just ldapPool | Just upsertMode' <- upsertMode -> do - ldapData <- campusUser ldapConf ldapPool Creds{..} + ldapData <- campusUser ldapPool campusUserFailoverMode Creds{..} $logDebugS "LDAP" $ "Successful LDAP lookup: " <> tshow ldapData Authenticated . entityKey <$> upsertCampusUser upsertMode' ldapData _other -> acceptExisting authPlugins (UniWorX{ appSettings' = AppSettings{..}, appLdapPool }) = catMaybes - [ campusLogin <$> appLdapConf <*> appLdapPool + [ flip campusLogin campusUserFailoverMode <$> appLdapPool , Just . hashLogin $ pwHashAlgorithm appAuthPWHash , dummyLogin <$ guard appAuthDummyLogin ] @@ -4607,8 +4958,12 @@ instance YesodAuth UniWorX where _other -> Auth.germanMessage where lang = Text.splitOn "-" $ selectLanguage' appLanguages ls +campusUserFailoverMode :: FailoverMode +campusUserFailoverMode = FailoverUnlimited + instance YesodAuthPersist UniWorX + unsafeHandler :: UniWorX -> Handler a -> IO a unsafeHandler f h = do logger <- makeLogger f @@ -4649,3 +5004,8 @@ instance {-# OVERLAPPING #-} (Monad m, MonadHandler m, HandlerSite m ~ UniWorX) -- https://github.com/yesodweb/yesod/wiki/Sending-email -- https://github.com/yesodweb/yesod/wiki/Serve-static-files-from-a-separate-domain -- https://github.com/yesodweb/yesod/wiki/i18n-messages-in-the-scaffolding + + +embedRenderMessage ''UniWorX ''ButtonSubmit id + +embedRenderMessage ''UniWorX ''CampusUserConversionException id diff --git a/src/Foundation/I18n.hs b/src/Foundation/I18n.hs index b1ab71296..2b505386c 100644 --- a/src/Foundation/I18n.hs +++ b/src/Foundation/I18n.hs @@ -277,6 +277,15 @@ instance RenderMessage UniWorX (Either ExamPassed ExamGrade) where mr :: RenderMessage UniWorX msg => msg -> Text mr = renderMessage foundation ls +instance RenderMessage UniWorX CourseParticipantState where + renderMessage foundation ls = \case + CourseParticipantActive -> mr MsgCourseParticipantActive + CourseParticipantInactive False -> mr MsgCourseParticipantInactive + CourseParticipantInactive True -> mr MsgCourseParticipantNoShow + where + mr :: RenderMessage UniWorX msg => msg -> Text + mr = renderMessage foundation ls + -- ToMessage instances for converting raw numbers to Text (no internationalization) instance ToMessage Int where diff --git a/src/Foundation/Type.hs b/src/Foundation/Type.hs index f6292c4f9..6dd5305f6 100644 --- a/src/Foundation/Type.hs +++ b/src/Foundation/Type.hs @@ -1,19 +1,20 @@ module Foundation.Type ( UniWorX(..) + , SomeSessionStorage(..) + , _SessionStorageMemcachedSql, _SessionStorageAcid , SMTPPool - , _appSettings', _appStatic, _appConnPool, _appSmtpPool, _appLdapPool, _appWidgetMemcached, _appHttpManager, _appLogger, _appLogSettings, _appCryptoIDKey, _appClusterID, _appInstanceID, _appJobState, _appSessionKey, _appSecretBoxKey, _appJSONWebKeySet, _appHealthReport + , _appSettings', _appStatic, _appConnPool, _appSmtpPool, _appLdapPool, _appWidgetMemcached, _appHttpManager, _appLogger, _appLogSettings, _appCryptoIDKey, _appClusterID, _appInstanceID, _appJobState, _appSessionStore, _appSecretBoxKey, _appJSONWebKeySet, _appHealthReport ) where import Import.NoFoundation import Database.Persist.Sql (ConnectionPool) -import qualified Web.ClientSession as ClientSession - import Jobs.Types import Yesod.Core.Types (Logger) import qualified Crypto.Saltine.Core.SecretBox as SecretBox +import qualified Crypto.Saltine.Core.AEAD as AEAD import qualified Jose.Jwk as Jose import qualified Database.Memcached.Binary.IO as Memcached @@ -21,36 +22,47 @@ import qualified Database.Memcached.Binary.IO as Memcached type SMTPPool = Pool SMTPConnection +data SomeSessionStorage + = SessionStorageMemcachedSql { sessionStorageMemcachedSql :: MemcachedSqlStorage SessionMap } + | SessionStorageAcid { sessionStorageAcid :: AcidStorage SessionMap } + +makePrisms ''SomeSessionStorage + -- | The foundation datatype for your application. This can be a good place to -- keep settings and values requiring initialization before your application -- starts running, such as database connections. Every handler will have -- access to the data present here. data UniWorX = UniWorX - { appSettings' :: AppSettings - , appStatic :: EmbeddedStatic -- ^ Settings for static file serving. - , appConnPool :: ConnectionPool -- ^ Database connection pool. - , appSmtpPool :: Maybe SMTPPool - , appLdapPool :: Maybe LdapPool - , appWidgetMemcached :: Maybe Memcached.Connection -- ^ Actually a proper pool - , appHttpManager :: Manager - , appLogger :: (ReleaseKey, TVar Logger) - , appLogSettings :: TVar LogSettings - , appCryptoIDKey :: CryptoIDKey - , appClusterID :: ClusterId - , appInstanceID :: InstanceId - , appJobState :: TMVar JobState - , appSessionKey :: ClientSession.Key - , appSecretBoxKey :: SecretBox.Key - , appJSONWebKeySet :: Jose.JwkSet - , appHealthReport :: TVar (Set (UTCTime, HealthReport)) + { appSettings' :: AppSettings + , appStatic :: EmbeddedStatic -- ^ Settings for static file serving. + , appConnPool :: ConnectionPool -- ^ Database connection pool. + , appSmtpPool :: Maybe SMTPPool + , appLdapPool :: Maybe (Failover (LdapConf, LdapPool)) + , appWidgetMemcached :: Maybe Memcached.Connection -- ^ Actually a proper pool + , appHttpManager :: Manager + , appLogger :: (ReleaseKey, TVar Logger) + , appLogSettings :: TVar LogSettings + , appCryptoIDKey :: CryptoIDKey + , appClusterID :: ClusterId + , appInstanceID :: InstanceId + , appJobState :: TMVar JobState + , appSessionStore :: SomeSessionStorage + , appSecretBoxKey :: SecretBox.Key + , appJSONWebKeySet :: Jose.JwkSet + , appHealthReport :: TVar (Set (UTCTime, HealthReport)) + , appMemcached :: Maybe (AEAD.Key, Memcached.Connection) } makeLenses_ ''UniWorX instance HasInstanceID UniWorX InstanceId where instanceID = _appInstanceID +instance HasClusterID UniWorX ClusterId where + clusterID = _appClusterID instance HasJSONWebKeySet UniWorX Jose.JwkSet where jsonWebKeySet = _appJSONWebKeySet instance HasHttpManager UniWorX Manager where httpManager = _appHttpManager instance HasAppSettings UniWorX where appSettings = _appSettings' +instance HasCookieSettings RegisteredCookie UniWorX where + getCookieSettings = appCookieSettings . appSettings' diff --git a/src/Foundation/Types.hs b/src/Foundation/Types.hs new file mode 100644 index 000000000..4e21dce2f --- /dev/null +++ b/src/Foundation/Types.hs @@ -0,0 +1,17 @@ +module Foundation.Types + ( UpsertCampusUserMode(..) + , _UpsertCampusUser, _UpsertCampusUserDummy, _UpsertCampusUserOther + , _upsertCampusUserIdent + ) where + +import Import.NoFoundation + + +data UpsertCampusUserMode + = UpsertCampusUser + | UpsertCampusUserDummy { upsertCampusUserIdent :: UserIdent } + | UpsertCampusUserOther { uspertCampusUserIdent :: UserIdent } + deriving (Eq, Ord, Read, Show, Generic, Typeable) + +makeLenses_ ''UpsertCampusUserMode +makePrisms ''UpsertCampusUserMode diff --git a/src/Handler/Admin.hs b/src/Handler/Admin.hs index 0002200e8..0baadf2b8 100644 --- a/src/Handler/Admin.hs +++ b/src/Handler/Admin.hs @@ -9,6 +9,7 @@ import Handler.Utils import Handler.Admin.Test as Handler.Admin import Handler.Admin.ErrorMessage as Handler.Admin import Handler.Admin.StudyFeatures as Handler.Admin +import Handler.Admin.Tokens as Handler.Admin getAdminR :: Handler Html diff --git a/src/Handler/Admin/ErrorMessage.hs b/src/Handler/Admin/ErrorMessage.hs index 5de72e683..64d0d538c 100644 --- a/src/Handler/Admin/ErrorMessage.hs +++ b/src/Handler/Admin/ErrorMessage.hs @@ -6,8 +6,6 @@ import Import import Handler.Utils import Data.Aeson.Encode.Pretty (encodePrettyToTextBuilder) -import Control.Monad.Trans.Except - getAdminErrMsgR, postAdminErrMsgR :: Handler Html getAdminErrMsgR = postAdminErrMsgR diff --git a/src/Handler/Admin/StudyFeatures.hs b/src/Handler/Admin/StudyFeatures.hs index f07bdc979..e21ae568b 100644 --- a/src/Handler/Admin/StudyFeatures.hs +++ b/src/Handler/Admin/StudyFeatures.hs @@ -330,7 +330,6 @@ postAdminFeaturesR = do [ sortable (Just "key") (i18nCell MsgGenericKey) (numCell . view (_dbrOutput . _entityVal . _studyDegreeKey)) , sortable (Just "name") (i18nCell MsgDegreeName) (textInputCell _1 (_dbrOutput . _entityVal . _studyDegreeName) (_dbrOutput . _entityKey)) , sortable (Just "short") (i18nCell MsgDegreeShort) (textInputCell _2 (_dbrOutput . _entityVal . _studyDegreeShorthand) (_dbrOutput . _entityKey)) - , dbRow ] dbtSorting = Map.fromList [ ("key" , SortColumn (E.^. StudyDegreeKey)) @@ -356,13 +355,13 @@ postAdminFeaturesR = do dbtSQLQuery = return dbtRowKey = (E.^. StudyTermsKey) dbtProj field@(view _dbrOutput -> Entity fId _) = do - fieldSchools <- fmap (setOf $ folded . _Value) . lift . E.select . E.from $ \school -> do + fieldSchools <- fmap (setOf $ folded . _Value) . E.select . E.from $ \school -> do E.where_ . E.exists . E.from $ \schoolTerms -> E.where_ $ schoolTerms E.^. SchoolTermsSchool E.==. school E.^. SchoolId E.&&. schoolTerms E.^. SchoolTermsTerms E.==. E.val fId E.where_ $ school E.^. SchoolShorthand `E.in_` E.valList (toListOf (folded . _entityKey . _SchoolId) schools) return $ school E.^. SchoolId - fieldParents <- fmap (setOf folded) . lift . E.select . E.from $ \terms -> do + fieldParents <- fmap (setOf folded) . E.select . E.from $ \terms -> do E.where_ . E.exists . E.from $ \subTerms -> E.where_ $ subTerms E.^. StudySubTermsChild E.==. E.val fId E.&&. subTerms E.^. StudySubTermsParent E.==. terms E.^. StudyTermsId @@ -379,7 +378,6 @@ postAdminFeaturesR = do , sortable (Just "field-type") (i18nCell MsgStudyTermsDefaultFieldType) (fieldTypeCell _6 (_dbrOutput . _1 . _entityVal . _studyTermsDefaultType) _dbrKey') , flip foldMap schools $ \(Entity ssh School{schoolName}) -> sortable Nothing (cell $ toWidget schoolName) (checkboxCell (_3 . at ssh . _Maybe) (_dbrOutput . _3 . at ssh . _Maybe) _dbrKey') - , dbRow ] dbtSorting = Map.fromList [ ("key" , SortColumn $ queryField >>> (E.^. StudyTermsKey)) @@ -416,8 +414,7 @@ postAdminFeaturesR = do dbtRowKey = (E.^. StudyTermNameCandidateId) dbtProj = return dbtColonnade = dbColonnade $ mconcat - [ dbRow - , sortable (Just "key") (i18nCell MsgStudyTermsKey) (numCell . view (_dbrOutput . _entityVal . _studyTermNameCandidateKey)) + [ sortable (Just "key") (i18nCell MsgStudyTermsKey) (numCell . view (_dbrOutput . _entityVal . _studyTermNameCandidateKey)) , sortable (Just "name") (i18nCell MsgStudyTermsName) (textCell . view (_dbrOutput . _entityVal . _studyTermNameCandidateName)) , sortable (Just "incidence") (i18nCell MsgStudyCandidateIncidence) (pathPieceCell . view (_dbrOutput . _entityVal . _studyTermNameCandidateIncidence)) ] @@ -459,8 +456,7 @@ postAdminFeaturesR = do dbtRowKey = queryCandidate >>> (E.^. StudySubTermParentCandidateId) dbtProj = return dbtColonnade = dbColonnade $ mconcat - [ dbRow - , sortable (Just "child") (i18nCell MsgStudySubTermsChildKey) (numCell . view (_dbrOutput . _1 . _entityVal . _studySubTermParentCandidateKey)) + [ sortable (Just "child") (i18nCell MsgStudySubTermsChildKey) (numCell . view (_dbrOutput . _1 . _entityVal . _studySubTermParentCandidateKey)) , sortable (Just "child-name") (i18nCell MsgStudySubTermsChildName) (maybe mempty i18nCell . preview (_dbrOutput . _3 . _Just . _entityVal . _studyTermsName . _Just)) , sortable (Just "parent") (i18nCell MsgStudySubTermsParentKey) (numCell . view (_dbrOutput . _1 . _entityVal . _studySubTermParentCandidateParent)) , sortable (Just "parent-name") (i18nCell MsgStudySubTermsParentName) (maybe mempty i18nCell . preview (_dbrOutput . _2 . _Just . _entityVal . _studyTermsName . _Just)) @@ -501,8 +497,7 @@ postAdminFeaturesR = do dbtRowKey = queryCandidate >>> (E.^. StudyTermStandaloneCandidateId) dbtProj = return dbtColonnade = formColonnade $ mconcat - [ dbRow - , sortable (Just "key") (i18nCell MsgStudyTermsKey) (numCell . view (_dbrOutput . _1 . _entityVal . _studyTermStandaloneCandidateKey)) + [ sortable (Just "key") (i18nCell MsgStudyTermsKey) (numCell . view (_dbrOutput . _1 . _entityVal . _studyTermStandaloneCandidateKey)) , sortable (Just "name") (i18nCell MsgStudyTermsName) (maybe mempty i18nCell . preview (_dbrOutput . _2 . _Just . _entityVal . _studyTermsName . _Just)) , sortable (Just "incidence") (i18nCell MsgStudyCandidateIncidence) (pathPieceCell . view (_dbrOutput . _1 . _entityVal . _studyTermStandaloneCandidateIncidence)) , sortable Nothing (i18nCell MsgStudyTermsDefaultDegree) (degreeCell _1 (pre $ _dbrOutput . _2 . _Just . _studyTermsDefaultDegree . _Just) _dbrKey') diff --git a/src/Handler/Admin/Tokens.hs b/src/Handler/Admin/Tokens.hs new file mode 100644 index 000000000..90b6003d4 --- /dev/null +++ b/src/Handler/Admin/Tokens.hs @@ -0,0 +1,101 @@ +module Handler.Admin.Tokens + ( getAdminTokensR, postAdminTokensR + ) where + +import Import +import Handler.Utils + +import qualified Data.HashSet as HashSet +import qualified Data.HashMap.Strict as HashMap +import qualified Data.Set as Set + +import qualified Data.Aeson as Aeson +import qualified Data.Aeson.Encode.Pretty as Aeson + +import Data.Map ((!), (!?)) + +import qualified Data.Text as Text + + +data BearerTokenForm = BearerTokenForm + { btfAuthority :: HashSet (Either UserGroupName UserId) + , btfRoutes :: Maybe (HashSet (Route UniWorX)) + , btfRestrict :: HashMap (Route UniWorX) Value + , btfAddAuth :: Maybe AuthDNF + , btfExpiresAt :: Maybe (Maybe UTCTime) + , btfStartsAt :: Maybe UTCTime + } + +bearerTokenForm :: WForm Handler (FormResult BearerTokenForm) +bearerTokenForm = do + muid <- maybeAuthId + + btfAuthorityGroups <- aFormToWForm $ HashSet.fromList . map Left <$> massInputListA pathPieceField (const "") (\p -> Just . SomeRoute $ AdminTokensR :#: p) ("token-groups" :: Text) (fslI MsgBearerTokenAuthorityGroups & setTooltip MsgBearerTokenAuthorityGroupsTip) False Nothing + btfAuthorityUsers <- fmap (fmap . ofoldMap $ HashSet.singleton . Right) <$> wopt (checkMap (foldMapM $ fmap Set.singleton . left MsgBearerTokenAuthorityUnknownUser) (Set.map Right) $ multiUserField False Nothing) (fslI MsgBearerTokenAuthorityUsers & setTooltip MsgBearerTokenAuthorityUsersTip) (Just $ Set.singleton <$> muid) + let btfAuthority' :: FormResult (HashSet (Either UserGroupName UserId)) + btfAuthority' + = (<>) <$> btfAuthorityGroups <*> ((hoistMaybe =<< btfAuthorityUsers) <|> pure HashSet.empty) + + let btfRoutesForm = HashSet.fromList <$> massInputListA routeField (const "") (\p -> Just . SomeRoute $ AdminTokensR :#: p) ("token-routes" :: Text) (fslI MsgBearerTokenRoutes & setTooltip MsgBearerTokenRoutesTip) True Nothing + btfRoutes' <- optionalActionW btfRoutesForm (fslI MsgBearerTokenRestrictRoutes) (Just True) + + let btfRestrictForm = massInputAccumEditW miAdd' miCell' (\p -> Just . SomeRoute $ AdminTokensR :#: p) miLayout' ("token-restrictions" :: Text) (fslI MsgBearerTokenRestrictions) False Nothing + where miAdd' nudge = fmap (over (mapped . _1) tweakRes) . miForm nudge . Left + where tweakRes res = res <&> \(newRoute, newRestr) oldRestrs -> pure (bool [(newRoute, newRestr)] [] $ newRoute `HashMap.member` HashMap.fromList oldRestrs) + miCell' nudge = miForm nudge . Right + miForm :: (Text -> Text) + -> Either (FieldView UniWorX) (Route UniWorX, Value) + -> Form (Route UniWorX, Value) + miForm nudge mode csrf = do + (routeRes, routeView) <- mpreq routeField ("" & addName (nudge "route")) (mode ^? _Right . _1) + (restrRes, restrView) <- mpreq (checkMap (left Text.pack . Aeson.eitherDecode . encodeUtf8 . fromStrict . unTextarea) (Textarea . toStrict . decodeUtf8 . Aeson.encodePretty) textareaField) ("" & addName (nudge "restr")) (mode ^? _Right . _2) + + return ((,) <$> routeRes <*> restrRes, case mode of + Left btn -> $(widgetFile "widgets/massinput/token-restrictions/add") + Right _ -> $(widgetFile "widgets/massinput/token-restrictions/cell") + ) + + miLayout' :: MassInputLayout ListLength (Route UniWorX, Value) (Route UniWorX, Value) + miLayout' lLength _ cellWdgts delButtons addWdgts = $(widgetFile "widgets/massinput/token-restrictions/layout") + + btfRestrict' <- fmap HashMap.fromList <$> btfRestrictForm + + btfAddAuth' <- fmap (assertM $ not . Set.null . dnfTerms) <$> wopt pathPieceField (fslI MsgBearerTokenAdditionalAuth & setTooltip MsgBearerTokenAdditionalAuthTip) Nothing + + btfExpiresAt' <- optionalActionW (aopt utcTimeField (fslI MsgBearerTokenExpires & setTooltip MsgBearerTokenExpiresTip) Nothing) (fslI MsgBearerTokenOverrideExpiration) (Just False) + btfStartsAt' <- wopt utcTimeField (fslI MsgBearerTokenOverrideStart & setTooltip MsgBearerTokenOverrideStartTip) Nothing + + return $ BearerTokenForm + <$> btfAuthority' + <*> btfRoutes' + <*> btfRestrict' + <*> btfAddAuth' + <*> btfExpiresAt' + <*> btfStartsAt' + + +getAdminTokensR, postAdminTokensR :: Handler Html +getAdminTokensR = postAdminTokensR +postAdminTokensR = do + ((bearerReq, bearerView), bearerEnc) <- runFormPost $ renderWForm FormStandard bearerTokenForm + + mjwt <- formResultMaybe bearerReq $ \BearerTokenForm{..} -> do + uid <- requireAuthId + let btfAuthority' = btfAuthority + & HashSet.insert (Right uid) + & HashSet.map (left toJSON) + + fmap Just . encodeBearer . set _bearerRestrictions btfRestrict =<< bearerToken btfAuthority' btfRoutes btfAddAuth btfExpiresAt btfStartsAt + + siteLayoutMsg' MsgMenuAdminTokens $ do + setTitleI MsgMenuAdminTokens + + let bearerForm = wrapForm bearerView def + { formMethod = POST + , formAction = Just $ SomeRoute AdminTokensR + , formEncoding = bearerEnc + } + + warning <- notification NotificationBroad <$> messageI Warning MsgBearerTokenUsageWarning + + $(widgetFile "admin-tokens") diff --git a/src/Handler/Allocation.hs b/src/Handler/Allocation.hs index 9ff9b336a..5162e86a5 100644 --- a/src/Handler/Allocation.hs +++ b/src/Handler/Allocation.hs @@ -9,3 +9,5 @@ import Handler.Allocation.Register as Handler.Allocation import Handler.Allocation.List as Handler.Allocation import Handler.Allocation.Users as Handler.Allocation import Handler.Allocation.Prios as Handler.Allocation +import Handler.Allocation.Compute as Handler.Allocation +import Handler.Allocation.Accept as Handler.Allocation diff --git a/src/Handler/Allocation/Accept.hs b/src/Handler/Allocation/Accept.hs new file mode 100644 index 000000000..59ea952d2 --- /dev/null +++ b/src/Handler/Allocation/Accept.hs @@ -0,0 +1,162 @@ +module Handler.Allocation.Accept + ( SessionDataAllocationResults(..) + , AllocationAcceptButton(..) + , allocationAcceptForm + , getAAcceptR, postAAcceptR + ) where + +import Import +import Handler.Utils +import Handler.Utils.Allocation + +import Data.Map ((!?)) +import qualified Data.Map as Map + +import qualified Database.Esqueleto as E +import qualified Control.Monad.State.Class as State + +import Data.Sequence (Seq((:|>))) + + +newtype SessionDataAllocationResults = SessionDataAllocationResults + { getSessionDataAllocationResults :: Map ( TermId + , SchoolId + , AllocationShorthand + ) + ( UTCTime + , AllocationFingerprint + , Set (UserId, CourseId) + , Seq MatchingLogRun + ) + } deriving (Eq, Ord, Read, Show, Generic, Typeable) + deriving newtype (ToJSON, FromJSON) + deriving (Monoid, Semigroup) via Dual (Map (TermId, SchoolId, AllocationShorthand) (UTCTime, AllocationFingerprint, Set (UserId, CourseId), Seq MatchingLogRun)) + +makeWrapped ''SessionDataAllocationResults + + +data AllocationAcceptButton + = BtnAllocationAccept + deriving (Eq, Ord, Read, Show, Enum, Bounded, Generic, Typeable) + deriving anyclass (Universe, Finite) + +nullaryPathPiece ''AllocationAcceptButton $ camelToPathPiece' 2 +embedRenderMessage ''UniWorX ''AllocationAcceptButton id + +instance Button UniWorX AllocationAcceptButton where + btnClasses BtnAllocationAccept = [BCIsButton, BCPrimary] + + +allocationAcceptForm :: AllocationId -> DB (Maybe (Form (UTCTime, AllocationFingerprint, Set (UserId, CourseId), Seq MatchingLogRun))) +allocationAcceptForm aId = runMaybeT $ do + Allocation{..} <- MaybeT $ get aId + SessionDataAllocationResults allocMap <- MaybeT $ lookupSessionJson SessionAllocationResults + allocRes@(allocTime, allocFp, allocMatching, _ :|> MatchingLogRun{..}) <- hoistMaybe $ allocMap !? (allocationTerm, allocationSchool, allocationShorthand) + + allocationUsers <- fmap (map $ bimap E.unValue E.unValue) . lift . E.select . E.from $ \allocationUser -> do + E.where_ $ allocationUser E.^. AllocationUserAllocation E.==. E.val aId + E.&&. E.not_ (E.isNothing $ allocationUser E.^. AllocationUserPriority) + let applications = E.subSelectCount . E.from $ \courseApplication -> + E.where_ $ courseApplication E.^. CourseApplicationAllocation E.==. E.val (Just aId) + E.&&. courseApplication E.^. CourseApplicationUser E.==. allocationUser E.^. AllocationUserUser + return . (allocationUser E.^. AllocationUserUser, ) $ E.case_ + [ E.when_ (E.castNum (allocationUser E.^. AllocationUserTotalCourses) E.>. applications) + E.then_ (applications :: E.SqlExpr (E.Value Int)) + ] + (E.else_ . E.castNum $ allocationUser E.^. AllocationUserTotalCourses) + let allocationPlacesRequested = sumOf (folded . _2) allocationUsers + userAllocations = ofoldr (\(uid, _cid) -> Map.insertWith (+) uid 1) Map.empty allocMatching + + allocationUsers' <- hoistMaybe $ + let (res, leftoverAllocs) = foldr (\user@(uid, _) (acc, allocCounts) + -> ( (user, Map.findWithDefault 0 uid allocCounts) : acc + , Map.delete uid allocCounts + )) + ([] , userAllocations) allocationUsers + in guardOn (null leftoverAllocs) res :: Maybe [((UserId, Int), Integer)] + + let unmatchedUsers = olength $ filter ((<= 0) . view _2) allocationUsers' + + allocationCourses <- fmap (map $ over _3 E.unValue) . lift . E.select . E.from $ \(allocationCourse `E.InnerJoin` course) -> do + E.on $ allocationCourse E.^. AllocationCourseCourse E.==. course E.^. CourseId + E.&&. allocationCourse E.^. AllocationCourseAllocation E.==. E.val aId + let participants = E.subSelectCount . E.from $ \courseParticipant -> + E.where_ $ courseParticipant E.^. CourseParticipantCourse E.==. course E.^. CourseId + E.&&. courseParticipant E.^. CourseParticipantState E.==. E.val CourseParticipantActive + return (allocationCourse, course, participants) + let allocationCapacity = sumOf (folded . _2 . _entityVal . _courseCapacity . _Just) allocationCourses + + let courseAllocations = ofoldr (\(_uid, cid) -> Map.insertWith (+) cid 1) Map.empty allocMatching + allocationCourses' <- hoistMaybe $ + let (res, leftoverAllocs) = foldr (\course@(_, Entity cid _, _) (acc, allocCounts) + -> ( (course, Map.findWithDefault 0 cid allocCounts) : acc + , Map.delete cid allocCounts + )) + ([] , courseAllocations) allocationCourses + in guardOn (null leftoverAllocs) res :: Maybe [((Entity AllocationCourse, Entity Course, Int), Int)] + + let unmatchedCourses = olength $ filter ((<= 0) . view _2) allocationCourses' + + let validateMatches = + guardValidation MsgAllocationAcceptFormDoesNotMatchSession =<< State.get + + return . set (mapped . mapped . _1 . mapped) allocRes . validateForm validateMatches . identifyForm FIDAllocationAccept $ \csrf -> do + (prevAllocRes, prevAllocView) <- mreq hiddenField "" $ Just allocFp + let prevAllocMatches = (== allocFp) <$> prevAllocRes + + let + showTerms + | [_] <- nubOn (view $ _1 . _2 . _entityVal . _courseTerm) allocationCourses' + = False + | otherwise + = True + showSchools + | [_] <- nubOn (view $ _1 . _2 . _entityVal . _courseSchool) allocationCourses' + = False + | otherwise + = True + optimumAllocated = round . (* optimumProportion) . fromIntegral + where optimumProportion :: Rational + optimumProportion + | allocationCapacity == 0 = 0 + | otherwise = fromIntegral allocationPlacesRequested % fromIntegral allocationCapacity + allocHeat capN + = invDualHeat (optimumAllocated capN) capN + degenerateHeat capN + = capN <= optimumAllocated capN + + return (prevAllocMatches, $(widgetFile "allocation/accept")) + +getAAcceptR, postAAcceptR :: TermId -> SchoolId -> AllocationShorthand -> Handler Html +getAAcceptR = postAAcceptR +postAAcceptR tid ssh ash = do + (((_, acceptView), acceptEnctype), didStore) <- runDB $ do + aId <- getKeyBy404 $ TermSchoolAllocationShort tid ssh ash + + acceptForm <- maybe (redirect $ AllocationR tid ssh ash AComputeR) return =<< allocationAcceptForm aId + + formRes@((acceptRes, _), _) <- liftHandler $ runFormPost acceptForm + + didStore <- formResultMaybe acceptRes $ \(now, allocFp, allocMatchings, allocLog) -> do + modifySessionJson SessionAllocationResults . fmap (assertM $ not . views _Wrapped onull) . over (mapped . _Wrapped :: Setter' (Maybe SessionDataAllocationResults) _) $ + Map.filterWithKey (\(tid', ssh', ash') (_, allocFp', _, _) -> + or [ tid' /= tid + , ssh' /= ssh + , ash' /= ash + , allocFp' /= allocFp + ]) + storeAllocationResult aId now (allocFp, allocMatchings, allocLog) + return $ Just () + + return (formRes, is _Just didStore) + + when didStore $ do + addMessageI Success MsgAllocationAccepted + redirect $ AllocationR tid ssh ash AUsersR + + siteLayoutMsg MsgMenuAllocationAccept $ do + setTitleI MsgMenuAllocationAccept + + wrapForm' BtnAllocationAccept acceptView def + { formEncoding = acceptEnctype + } diff --git a/src/Handler/Allocation/Compute.hs b/src/Handler/Allocation/Compute.hs new file mode 100644 index 000000000..9c8b300e6 --- /dev/null +++ b/src/Handler/Allocation/Compute.hs @@ -0,0 +1,131 @@ +module Handler.Allocation.Compute + ( getAComputeR + , postAComputeR + ) where + +import Import + +import Handler.Utils +import Handler.Utils.Allocation +import Handler.Allocation.Accept (SessionDataAllocationResults(..)) + +import qualified Data.Set as Set +import qualified Data.Map as Map + +import qualified Database.Esqueleto as E + +import qualified Control.Monad.State.Class as State + + +data AllocationComputeForm = AllocationComputeForm + { acfMissingPrioritiesOk :: Set UserId + , acfRestrictCourses :: Maybe (Set CourseId) + } + +data AllocationComputeButton + = BtnAllocationCompute + deriving (Eq, Ord, Read, Show, Enum, Bounded, Generic, Typeable) + deriving anyclass (Universe, Finite) + +nullaryPathPiece ''AllocationComputeButton $ camelToPathPiece' 2 +embedRenderMessage ''UniWorX ''AllocationComputeButton id + +instance Button UniWorX AllocationComputeButton where + btnClasses BtnAllocationCompute = [BCIsButton, BCPrimary] + +missingPrioritiesUsers :: AllocationId -> DB (Map UserId User) +missingPrioritiesUsers aId = $cachedHereBinary aId $ do + usersWithoutPrio <- E.select . E.from $ \(user `E.InnerJoin` allocationUser) -> do + E.on $ user E.^. UserId E.==. allocationUser E.^. AllocationUserUser + E.&&. allocationUser E.^. AllocationUserAllocation E.==. E.val aId + + -- Ignore users without applications + E.where_ . E.exists . E.from $ \courseApplication -> do + E.where_ $ courseApplication E.^. CourseApplicationAllocation E.==. E.just (E.val aId) + E.&&. courseApplication E.^. CourseApplicationUser E.==. user E.^. UserId + E.where_ . E.exists . E.from $ \allocationCourse -> + E.where_ $ allocationCourse E.^. AllocationCourseCourse E.==. courseApplication E.^. CourseApplicationCourse + E.&&. allocationCourse E.^. AllocationCourseAllocation E.==. E.val aId + + E.where_ . E.isNothing $ allocationUser E.^. AllocationUserPriority + + return user + + return $ toMapOf (folded .> _entityVal) usersWithoutPrio + +missingPriorities :: AllocationId -> AForm DB (Set UserId) +missingPriorities aId = wFormToAForm $ do + usersWithoutPrio <- lift . lift $ missingPrioritiesUsers aId + + let missingPriosField = checkBoxField { fieldView = missingPriosFieldView } + where + missingPriosFieldView theId name attrs res isReq + = $(i18nWidgetFile "allocation-confirm-missing-prios") + where checkBoxFieldView = labeledCheckBoxView (i18n MsgAllocationUsersMissingPrioritiesOk) theId name attrs res isReq + + if + | null usersWithoutPrio + -> return $ pure Set.empty + | otherwise + -> fmap (bool Set.empty $ Map.keysSet usersWithoutPrio) <$> wpreq missingPriosField (fslI MsgAllocationUsersMissingPriorities & setTooltip MsgAllocationUsersMissingPrioritiesTip) (Just False) + + +restrictCourses :: (MonadHandler m, HandlerSite m ~ UniWorX) => AllocationId -> AForm m (Maybe (Set CourseId)) +restrictCourses aId = hoistAForm liftHandler $ + optionalActionA selectCourses (fslI MsgAllocationRestrictCourses & setTooltip MsgAllocationRestrictCoursesTip) (Just False) + where + selectCourses = courseSelectForm query coursePred miButtonAction' miIdent' fSettings fRequired mPrev + where + query = E.from $ \(course `E.InnerJoin` allocationCourse) -> do + E.on $ course E.^. CourseId E.==. allocationCourse E.^. AllocationCourseCourse + E.&&. allocationCourse E.^. AllocationCourseAllocation E.==. E.val aId + return course + coursePred _ = return True + mPrev = Nothing + fRequired = True + fSettings = fslI MsgAllocationRestrictCoursesSelection & setTooltip MsgAllocationRestrictCoursesSelectionTip + miIdent' :: Text + miIdent' = "course-selection" + miButtonAction' _ = Nothing + +allocationComputeForm :: AllocationId -> AForm DB AllocationComputeForm +allocationComputeForm aId = wFormToAForm $ do + onlyComputeMsg <- messageI Info MsgAllocationOnlyCompute + + aFormToWForm $ AllocationComputeForm + <$ aformMessage onlyComputeMsg + <*> missingPriorities aId + <*> restrictCourses aId + +validateAllocationComputeForm :: AllocationId -> FormValidator AllocationComputeForm DB () +validateAllocationComputeForm aId = do + usersWithoutPrio <- lift $ missingPrioritiesUsers aId + + missingOk <- State.gets acfMissingPrioritiesOk + guardValidation MsgAllocationUsersMissingPrioritiesNotOk $ + Map.keysSet usersWithoutPrio `Set.isSubsetOf` missingOk + + +getAComputeR, postAComputeR :: TermId -> SchoolId -> AllocationShorthand -> Handler Html +getAComputeR = postAComputeR +postAComputeR tid ssh ash = do + (_, ((_computeFormRes, computeFormView), computeFormEnctype)) <- runDB $ do + aEnt@(Entity aId _) <- getBy404 $ TermSchoolAllocationShort tid ssh ash + formRes@((computeFormRes, _), _) <- runFormPost . validateForm (validateAllocationComputeForm aId) . renderAForm FormStandard $ allocationComputeForm aId + + formResult computeFormRes $ \AllocationComputeForm{..} -> do + now <- liftIO getCurrentTime + (allocFp, allocMatching, allocLog) <- computeAllocation aEnt acfRestrictCourses + tellSessionJson SessionAllocationResults . SessionDataAllocationResults $ + Map.singleton (tid, ssh, ash) (now, allocFp, allocMatching, allocLog) + addMessageI Success MsgAllocationComputed + redirect $ AllocationR tid ssh ash AUsersR -- Redirect aborts transaction for safety + + return (aEnt, formRes) + + siteLayoutMsg MsgMenuAllocationCompute $ do + setTitleI MsgMenuAllocationCompute + + wrapForm' BtnAllocationCompute computeFormView def + { formEncoding = computeFormEnctype + } diff --git a/src/Handler/Allocation/List.hs b/src/Handler/Allocation/List.hs index 081734394..fc6d7e48a 100644 --- a/src/Handler/Allocation/List.hs +++ b/src/Handler/Allocation/List.hs @@ -69,7 +69,7 @@ getAllocationListR = do <*> view queryAvailable <*> view (maybe (to . const $ E.val 0) queryApplied muid) - dbtProj :: DBRow _ -> MaybeT (YesodDB UniWorX) AllocationTableData + dbtProj :: DBRow _ -> DB AllocationTableData dbtProj = return . over (_dbrOutput . _2) E.unValue . over (_dbrOutput . _3) E.unValue dbtRowKey = view $ queryAllocation . to (E.^. AllocationId) diff --git a/src/Handler/Allocation/Prios.hs b/src/Handler/Allocation/Prios.hs index 9edc03250..20b3f5127 100644 --- a/src/Handler/Allocation/Prios.hs +++ b/src/Handler/Allocation/Prios.hs @@ -57,8 +57,8 @@ postAPriosR tid ssh ash = do formResult priosRes $ \(mode, fInfo) -> do let sourcePrios = case mode of - AllocationPrioritiesNumeric -> fileSourceCsvPositional Csv.NoHeader fInfo - AllocationPrioritiesOrdinal -> fileSourceCsvPositional Csv.NoHeader fInfo .| C.map Csv.fromOnly .| ordinalPriorities + AllocationPrioritiesNumeric -> transPipe liftHandler fInfo .| fileSourceCsvPositional Csv.NoHeader + AllocationPrioritiesOrdinal -> transPipe liftHandler fInfo .| fileSourceCsvPositional Csv.NoHeader .| C.map Csv.fromOnly .| ordinalPriorities (matrSunk, matrMissing) <- runDB $ do Entity aId _ <- getBy404 $ TermSchoolAllocationShort tid ssh ash @@ -66,7 +66,15 @@ postAPriosR tid ssh ash = do [ AllocationUserAllocation ==. aId ] [ AllocationUserPriority =. Nothing ] matrSunk <- runConduit $ sourcePrios .| sinkAllocationPriorities aId - matrMissing <- fromIntegral <$> count [ AllocationUserAllocation ==. aId, AllocationUserPriority ==. Nothing ] + matrMissing <- E.selectCountRows . E.from $ \allocationUser -> do + E.where_ $ allocationUser E.^. AllocationUserAllocation E.==. E.val aId + E.&&. E.isNothing (allocationUser E.^. AllocationUserPriority) + + E.where_ . E.exists . E.from $ \(courseApplication `E.InnerJoin` allocationCourse) -> do + E.on $ allocationCourse E.^. AllocationCourseCourse E.==. courseApplication E.^. CourseApplicationCourse + E.&&. courseApplication E.^. CourseApplicationAllocation E.==. E.just (allocationCourse E.^. AllocationCourseAllocation) + E.where_ $ courseApplication E.^. CourseApplicationUser E.==. allocationUser E.^. AllocationUserUser + E.&&. courseApplication E.^. CourseApplicationAllocation E.==. E.just (E.val aId) return (matrSunk, matrMissing) when (matrSunk > 0) $ diff --git a/src/Handler/Allocation/Show.hs b/src/Handler/Allocation/Show.hs index 5d0adf9de..6015f2820 100644 --- a/src/Handler/Allocation/Show.hs +++ b/src/Handler/Allocation/Show.hs @@ -33,6 +33,7 @@ getAShowR tid ssh ash = do courses <- E.select . E.from $ \((allocationCourse `E.InnerJoin` course) `E.LeftOuterJoin` courseApplication `E.LeftOuterJoin` registration) -> do E.on $ registration E.?. CourseParticipantCourse E.==. E.just (course E.^. CourseId) E.&&. registration E.?. CourseParticipantUser E.==. E.val muid + E.&&. registration E.?. CourseParticipantState E.==. E.just (E.val CourseParticipantActive) E.on $ courseApplication E.?. CourseApplicationCourse E.==. E.just (course E.^. CourseId) E.&&. courseApplication E.?. CourseApplicationUser E.==. E.val muid E.&&. courseApplication E.?. CourseApplicationAllocation E.==. E.just (E.just $ E.val aId) diff --git a/src/Handler/Allocation/Users.hs b/src/Handler/Allocation/Users.hs index 829bc56f5..08260f683 100644 --- a/src/Handler/Allocation/Users.hs +++ b/src/Handler/Allocation/Users.hs @@ -6,6 +6,8 @@ module Handler.Allocation.Users import Import +import Handler.Allocation.Accept + import Handler.Utils import Handler.Utils.Allocation @@ -14,6 +16,12 @@ import qualified Database.Esqueleto.Utils as E import qualified Data.Csv as Csv +import Data.Map ((!?)) +import qualified Data.Map as Map +import qualified Data.Set as Set + +import Text.Blaze (toMarkup) + type UserTableExpr = E.SqlExpr (Entity User) `E.InnerJoin` E.SqlExpr (Entity AllocationUser) @@ -26,7 +34,9 @@ queryAllocationUser = to $(E.sqlIJproj 2 2) queryAppliedCourses :: Getter UserTableExpr (E.SqlExpr (E.Value Int)) queryAppliedCourses = queryAllocationUser . to queryAppliedCourses' - where queryAppliedCourses' allocationUser = E.subSelectCount . E.from $ \courseApplication -> + where queryAppliedCourses' allocationUser = E.subSelectCount . E.from $ \(courseApplication `E.InnerJoin` allocationCourse) -> do + E.on $ allocationCourse E.^. AllocationCourseCourse E.==. courseApplication E.^. CourseApplicationCourse + E.&&. courseApplication E.^. CourseApplicationAllocation E.==. E.just (allocationCourse E.^. AllocationCourseAllocation) E.where_ $ courseApplication E.^. CourseApplicationUser E.==. allocationUser E.^. AllocationUserUser E.&&. courseApplication E.^. CourseApplicationAllocation E.==. E.just (allocationUser E.^. AllocationUserAllocation) @@ -35,10 +45,13 @@ queryAssignedCourses = queryAllocationUser . to queryAssignedCourses' where queryAssignedCourses' allocationUser = E.subSelectCount . E.from $ \courseParticipant -> E.where_ $ courseParticipant E.^. CourseParticipantUser E.==. allocationUser E.^. AllocationUserUser E.&&. courseParticipant E.^. CourseParticipantAllocated E.==. E.just (allocationUser E.^. AllocationUserAllocation) + E.&&. courseParticipant E.^. CourseParticipantState E.==. E.val CourseParticipantActive queryVetoedCourses :: Getter UserTableExpr (E.SqlExpr (E.Value Int)) queryVetoedCourses = queryAllocationUser . to queryVetoedCourses' - where queryVetoedCourses' allocationUser = E.subSelectCount . E.from $ \courseApplication -> do + where queryVetoedCourses' allocationUser = E.subSelectCount . E.from $ \(courseApplication `E.InnerJoin` allocationCourse) -> do + E.on $ allocationCourse E.^. AllocationCourseCourse E.==. courseApplication E.^. CourseApplicationCourse + E.&&. courseApplication E.^. CourseApplicationAllocation E.==. E.just (allocationCourse E.^. AllocationCourseAllocation) E.where_ $ courseApplication E.^. CourseApplicationUser E.==. allocationUser E.^. AllocationUserUser E.&&. courseApplication E.^. CourseApplicationAllocation E.==. E.just (allocationUser E.^. AllocationUserAllocation) E.where_ $ courseApplication E.^. CourseApplicationRatingVeto @@ -103,10 +116,13 @@ instance CsvColumnsExplained AllocationUserTableCsv where getAUsersR, postAUsersR :: TermId -> SchoolId -> AllocationShorthand -> Handler Html getAUsersR = postAUsersR postAUsersR tid ssh ash = do - usersTable <- runDB $ do + (usersTable, acceptForm) <- runDB $ do Entity aId _ <- getBy404 $ TermSchoolAllocationShort tid ssh ash - now <- liftIO getCurrentTime - resultsDone <- (<= NTop (Just now)) . NTop <$> allocationDone aId + resultsDone <- is _Just <$> allocationStarted aId + allocMatching <- runMaybeT $ do + SessionDataAllocationResults allocMap <- MaybeT $ lookupSessionJson SessionAllocationResults + allocMatching <- fmap (view _3) . hoistMaybe $ allocMap !? (tid, ssh, ash) + return $ Map.fromListWith (<>) [ (uid, opoint cid) | (uid, cid) <- Set.toList allocMatching ] :: _ (Map UserId (NonNull (Set CourseId))) csvName <- getMessageRender <*> pure (MsgAllocationUsersCsvName tid ssh ash) @@ -136,48 +152,75 @@ postAUsersR tid ssh ash = do (,,,,) <$> view _1 <*> view _2 <*> view (_3 . _Value) <*> view (_4 . _Value) <*> view (_5 . _Value) dbtColonnade :: Colonnade Sortable _ _ - dbtColonnade = mconcat - [ colUserDisplayName $ resultUser . _entityVal . $(multifocusL 2) _userDisplayName _userSurname - , colUserMatriculation $ resultUser . _entityVal . _userMatrikelnummer - , colAllocationRequested $ resultAllocationUser . _entityVal . _allocationUserTotalCourses - , coursesModalApplied $ colAllocationApplied resultAppliedCourses - , coursesModalVetoed $ colAllocationVetoed resultVetoedCourses - , coursesModalAssigned . assignedHeated $ colAllocationAssigned resultAssignedCourses - , emptyOpticColonnade' emptyPriorityCell (resultAllocationUser . _entityVal . _allocationUserPriority . _Just) colAllocationPriority + dbtColonnade = mconcat . catMaybes $ + [ pure $ colUserDisplayName (resultUser . _entityVal . $(multifocusL 2) _userDisplayName _userSurname) + , pure $ colUserMatriculation (resultUser . _entityVal . _userMatrikelnummer) + , pure $ colAllocationRequested (resultAllocationUser . _entityVal . _allocationUserTotalCourses) + , pure . coursesModalApplied $ colAllocationApplied resultAppliedCourses + , pure . coursesModalVetoed $ colAllocationVetoed resultVetoedCourses + , guardOn resultsDone . coursesModalAssigned . bool id (assignedHeated $ view resultAssignedCourses) resultsDone $ colAllocationAssigned resultAssignedCourses + , coursesModalNewAssigned <$> do + allocMatching' <- allocMatching + let newAssigned uid = maybe 0 olength $ allocMatching' !? uid + pure . assignedHeated (views (resultUser . _entityKey) newAssigned) . sortable (Just "new-assigned") (i18nCell MsgAllocationUserNewMatches) . + views (resultUser . _entityKey) $ cell . toWidget . toMarkup . newAssigned + , pure $ emptyOpticColonnade' emptyPriorityCell (resultAllocationUser . _entityVal . _allocationUserPriority . _Just) colAllocationPriority ] where emptyPriorityCell = addCellClass ("table__td--center" :: Text) . cell $ messageTooltip =<< messageIconI Error IconMissingAllocationPriority MsgAllocationMissingPrioritiesIgnored - assignedHeated - | resultsDone = imapColonnade assignedHeated' - | otherwise = id + assignedHeated fAssigned = imapColonnade assignedHeated' where assignedHeated' res = let maxAssign = min (res ^. resultAllocationUser . _entityVal . _allocationUserTotalCourses . to fromIntegral) (res ^. resultAppliedCourses) - assigned = maxAssign - res ^. resultAssignedCourses + assigned = fAssigned res in cellAttrs <>~ [ ("class", "heated") - , ("style", [st|--hotness: #{tshow (heat maxAssign assigned)}|]) + , ("style", [st|--hotness: #{tshow (coHeat maxAssign assigned)}|]) ] coursesModalApplied = coursesModal $ \res -> E.from $ \(course `E.InnerJoin` courseApplication) -> do E.on $ course E.^. CourseId E.==. courseApplication E.^. CourseApplicationCourse E.where_ $ courseApplication E.^. CourseApplicationAllocation E.==. E.val (Just aId) E.&&. courseApplication E.^. CourseApplicationUser E.==. E.val (res ^. resultUser . _entityKey) E.orderBy [E.desc $ courseApplication E.^. CourseApplicationAllocationPriority] - return course + return ( course + , courseApplication E.^. CourseApplicationRatingPoints + , E.just $ courseApplication E.^. CourseApplicationRatingVeto + , E.exists . E.from $ \allocationCourse -> + E.where_ $ allocationCourse E.^. AllocationCourseCourse E.==. course E.^. CourseId + E.&&. allocationCourse E.^. AllocationCourseAllocation E.==. E.val aId + ) coursesModalVetoed = coursesModal $ \res -> E.from $ \(course `E.InnerJoin` courseApplication) -> do E.on $ course E.^. CourseId E.==. courseApplication E.^. CourseApplicationCourse E.where_ $ courseApplication E.^. CourseApplicationAllocation E.==. E.val (Just aId) E.&&. courseApplication E.^. CourseApplicationUser E.==. E.val (res ^. resultUser . _entityKey) E.where_ $ courseApplication E.^. CourseApplicationRatingVeto E.||. courseApplication E.^. CourseApplicationRatingPoints `E.in_` E.valList (map Just $ filter (view $ passingGrade . _Wrapped . to not) universeF) - return course + return ( course + , E.nothing + , E.nothing + , E.exists . E.from $ \allocationCourse -> + E.where_ $ allocationCourse E.^. AllocationCourseCourse E.==. course E.^. CourseId + E.&&. allocationCourse E.^. AllocationCourseAllocation E.==. E.val aId + ) coursesModalAssigned = coursesModal $ \res -> E.from $ \(course `E.InnerJoin` courseParticipant) -> do E.on $ courseParticipant E.^. CourseParticipantCourse E.==. course E.^. CourseId E.&&. courseParticipant E.^. CourseParticipantAllocated E.==. E.val (Just aId) E.where_ $ courseParticipant E.^. CourseParticipantUser E.==. E.val (res ^. resultUser . _entityKey) E.orderBy [E.asc $ courseParticipant E.^. CourseParticipantRegistration] - return course + return ( course + , E.nothing + , E.nothing + , courseParticipant E.^. CourseParticipantState E.==. E.val CourseParticipantActive + ) + coursesModalNewAssigned = coursesModal $ \res -> E.from $ \course -> do + E.where_ $ course E.^. CourseId `E.in_` E.valList (maybe [] otoList $ Map.lookup (res ^. resultUser . _entityKey) =<< allocMatching) + return ( course + , E.nothing + , E.nothing + , E.true + ) + coursesModal :: (_ -> E.SqlQuery (E.SqlExpr (Entity Course), E.SqlExpr (E.Value (Maybe ExamGrade)), E.SqlExpr (E.Value (Maybe Bool)), E.SqlExpr (E.Value Bool))) -> _ -> _ coursesModal courseSel = imapColonnade coursesModal' where coursesModal' res innerCell = review dbCell . (innerCell ^. cellAttrs, ) $ do @@ -185,19 +228,22 @@ postAUsersR tid ssh ash = do contents <- innerCell ^. cellContents return $ if | null courses -> contents - | otherwise -> $(widgetFile "table/cell/allocation-courses") + | otherwise -> let tooltipContent = $(widgetFile "table/cell/allocation-courses") + in $(widgetFile "widgets/tooltip_no-handle") dbtSorting = mconcat [ sortUserName' $ queryUser . $(multifocusG 2) (to (E.^. UserDisplayName)) (to (E.^. UserSurname)) - , sortUserMatriculation $ queryUser . (to (E.^. UserMatrikelnummer)) + , sortUserMatriculation $ queryUser . to (E.^. UserMatrikelnummer) , sortAllocationApplied queryAppliedCourses , sortAllocationAssigned queryAssignedCourses - , sortAllocationRequested $ queryAllocationUser . (to (E.^. AllocationUserTotalCourses)) + , sortAllocationRequested $ queryAllocationUser . to (E.^. AllocationUserTotalCourses) , sortAllocationVetoed queryVetoedCourses - , sortAllocationPriority $ queryAllocationUser . (to (E.^. AllocationUserPriority)) + , sortAllocationPriority $ queryAllocationUser . to (E.^. AllocationUserPriority) + , singletonMap "new-assigned" $ + SortProjected . comparing $ (\uid -> maybe 0 olength $ Map.lookup uid =<< allocMatching) . view (resultUser . _entityKey) ] dbtFilter = mconcat - [ fltrUserName' $ queryUser . (to (E.^. UserDisplayName)) - , fltrUserMatriculation $ queryUser . (to (E.^. UserMatrikelnummer)) + [ fltrUserName' $ queryUser . to (E.^. UserDisplayName) + , fltrUserMatriculation $ queryUser . to (E.^. UserMatrikelnummer) ] dbtFilterUI = mconcat [ fltrUserNameUI' @@ -220,12 +266,22 @@ postAUsersR tid ssh ash = do dbtCsvDecode = Nothing allocationUsersDBTableValidator = def & defaultSorting [SortAscBy "priority", SortAscBy "user-matriculation"] - & defaultPagesize PagesizeAll + & defaultPagesize (PagesizeLimit 500) usersTable <- dbTableDB' allocationUsersDBTableValidator allocationUsersDBTable - return usersTable + + acceptForm <- allocationAcceptForm aId + + return (usersTable, acceptForm) + + acceptView <- for acceptForm $ \acceptForm' -> do + (acceptWgt, acceptEnctype) <- generateFormPost acceptForm' + return $ wrapForm' BtnAllocationAccept acceptWgt def + { formAction = Just . SomeRoute $ AllocationR tid ssh ash AAcceptR + , formEncoding = acceptEnctype + } siteLayoutMsg MsgMenuAllocationUsers $ do setTitleI $ MsgAllocationUsersTitle tid ssh ash - usersTable + $(widgetFile "allocation/users") diff --git a/src/Handler/Corrections.hs b/src/Handler/Corrections.hs index 40abe8d14..ed70fa1f2 100644 --- a/src/Handler/Corrections.hs +++ b/src/Handler/Corrections.hs @@ -1,4 +1,19 @@ -module Handler.Corrections where +module Handler.Corrections + ( getCorrectionsR, postCorrectionsR + , getCCorrectionsR, postCCorrectionsR + , getSSubsR, postSSubsR + , getCorrectionR, postCorrectionR + , getCorrectionsUploadR, postCorrectionsUploadR + , getCorrectionsCreateR, postCorrectionsCreateR + , getCorrectionsGradeR, postCorrectionsGradeR + , getCAssignR, postCAssignR + , getSAssignR, postSAssignR + , correctionsR' + , ratedBy, courseIs, sheetIs, userIs + , colTerm, colSchool, colCourse, colSheet, colCorrector, colSubmissionLink, colSelect, colSubmittors, colSMatrikel, colRating, colAssigned, colRated, colPseudonyms, colRatedField, colPointsField, colMaxPointsField, colCommentField, colLastEdit + , makeCorrectionsTable + , ActionCorrections(..), downloadAction, deleteAction, assignAction, autoAssignAction + ) where import Import hiding (link) -- import System.FilePath (takeFileName) @@ -52,7 +67,7 @@ import qualified Data.Conduit.List as C type CorrectionTableExpr = (E.SqlExpr (Entity Course) `E.InnerJoin` E.SqlExpr (Entity Sheet) `E.InnerJoin` E.SqlExpr (Entity Submission)) `E.LeftOuterJoin` E.SqlExpr (Maybe (Entity User)) type CorrectionTableWhere = CorrectionTableExpr -> E.SqlExpr (E.Value Bool) -type CorrectionTableData = DBRow (Entity Submission, Entity Sheet, (CourseName, CourseShorthand, Key Term, Key School), Maybe (Entity User), Maybe UTCTime, Map UserId (User, Maybe Pseudonym)) +type CorrectionTableData = DBRow (Entity Submission, Entity Sheet, (CourseName, CourseShorthand, Key Term, Key School), Maybe (Entity User), Maybe UTCTime, Map UserId (User, Maybe Pseudonym, Maybe SubmissionGroupName), CryptoFileNameSubmission, Bool {- Access to non-anonymous submission data -}) correctionsTableQuery :: CorrectionTableWhere -> (CorrectionTableExpr -> v) -> CorrectionTableExpr -> E.SqlQuery v correctionsTableQuery whereClause returnStatement t@((course `E.InnerJoin` sheet `E.InnerJoin` submission) `E.LeftOuterJoin` corrector) = do @@ -71,9 +86,6 @@ lastEditQuery submission = E.subSelectMaybe $ E.from $ \edit -> do queryCourse :: CorrectionTableExpr -> E.SqlExpr (Entity Course) queryCourse = $(sqlIJproj 3 1) . $(sqlLOJproj 2 1) -querySheet :: CorrectionTableExpr -> E.SqlExpr (Entity Sheet) -querySheet = $(sqlIJproj 3 2) . $(sqlLOJproj 2 1) - querySubmission :: CorrectionTableExpr -> E.SqlExpr (Entity Submission) querySubmission = $(sqlIJproj 3 3) . $(sqlLOJproj 2 1) @@ -90,6 +102,12 @@ courseIs cid (( course `E.InnerJoin` _sheet `E.InnerJoin` _submission) `E.LeftO sheetIs :: Key Sheet -> CorrectionTableWhere sheetIs shid ((_course `E.InnerJoin` sheet `E.InnerJoin` _submission) `E.LeftOuterJoin` _corrector) = sheet E.^. SheetId E.==. E.val shid +userIs :: Key User -> CorrectionTableWhere +userIs uid ((_course `E.InnerJoin` _sheet `E.InnerJoin` submission) `E.LeftOuterJoin` _corrector) = E.exists . E.from $ \submissionUser -> + E.where_ $ submissionUser E.^. SubmissionUserSubmission E.==. submission E.^. SubmissionId + E.&&. submissionUser E.^. SubmissionUserUser E.==. E.val uid + + -- Columns colTerm :: IsDBTable m a => Colonnade Sortable CorrectionTableData (DBCell m a) @@ -104,7 +122,7 @@ colSchool = sortable (Just "school") (i18nCell MsgCourseSchool) colCourse :: IsDBTable m a => Colonnade Sortable CorrectionTableData (DBCell m a) colCourse = sortable (Just "course") (i18nCell MsgCourse) - $ \DBRow{ dbrOutput=(_, _, (_,csh,tid,sid),_ , _, _) } -> courseCellCL (tid,sid,csh) + $ \DBRow{ dbrOutput=(_, _, (_,csh,tid,sid),_ , _, _, _, _) } -> courseCellCL (tid,sid,csh) colSheet :: IsDBTable m a => Colonnade Sortable CorrectionTableData (DBCell m a) colSheet = sortable (Just "sheet") (i18nCell MsgSheet) $ \row -> @@ -116,51 +134,54 @@ colSheet = sortable (Just "sheet") (i18nCell MsgSheet) $ \row -> shn = sheetName $ entityVal sheet in anchorCell (CSheetR tid ssh csh shn SShowR) [whamlet|_{shn}|] -colSheetType :: IsDBTable m a => Colonnade Sortable CorrectionTableData (DBCell m a) -colSheetType = sortable (toNothing "sheetType") (i18nCell MsgSheetType) $ - i18nCell . sheetType <$> view (_dbrOutput . _2 . _entityVal) - -- \DBRow{ dbrOutput=(_, sheet, _, _, _, _) } -> i18nCell . sheetType $ entityVal sheet - colCorrector :: IsDBTable m a => Colonnade Sortable CorrectionTableData (DBCell m a) colCorrector = sortable (Just "corrector") (i18nCell MsgCorrector) $ \case - DBRow{ dbrOutput = (_, _, _, Nothing , _, _) } -> cell mempty - DBRow{ dbrOutput = (_, _, _, Just (Entity _ User{..}), _, _) } -> userCell userDisplayName userSurname + DBRow{ dbrOutput = (_, _, _, Nothing , _, _, _, _) } -> cell mempty + DBRow{ dbrOutput = (_, _, _, Just (Entity _ User{..}), _, _, _, _) } -> userCell userDisplayName userSurname colSubmissionLink :: IsDBTable m a => Colonnade Sortable CorrectionTableData (DBCell m a) -colSubmissionLink = sortable Nothing (i18nCell MsgSubmission) - $ \DBRow{ dbrOutput=(submission, sheet, course, _, _,_) } -> +colSubmissionLink = sortable (Just "submission") (i18nCell MsgSubmission) + $ \DBRow{ dbrOutput=(_, sheet, course, _, _,_, cid, _) } -> let csh = course ^. _2 tid = course ^. _3 ssh = course ^. _4 shn = sheetName $ entityVal sheet - mkCid = encrypt (entityKey submission :: SubmissionId) -- TODO: executed twice - mkRoute = do - cid <- mkCid - return $ CSubmissionR tid ssh csh shn cid SubShowR - in anchorCellM mkRoute (mkCid >>= \cid -> [whamlet|#{cid}|]) + in anchorCellC $cacheIdentHere (CSubmissionR tid ssh csh shn cid SubShowR) (toPathPiece cid) colSelect :: forall act h. (Semigroup act, Monoid act, Headedness h) => Colonnade h CorrectionTableData (DBCell _ (FormResult (act, DBFormResult CryptoFileNameSubmission Bool CorrectionTableData), SheetTypeSummary)) -colSelect = dbSelect (_1 . applying _2) id $ \DBRow{ dbrOutput=(Entity subId _, _, _, _, _, _) } -> encrypt subId +colSelect = dbSelect (_1 . applying _2) id $ \DBRow{ dbrOutput=(_, _, _, _, _, _, cid, _) } -> return cid colSubmittors :: IsDBTable m a => Colonnade Sortable CorrectionTableData (DBCell m a) -colSubmittors = sortable (Just "submittors") (i18nCell MsgSubmissionUsers) $ \DBRow{ dbrOutput=(_, _, course, _, _, users) } -> let - csh = course ^. _2 - tid = course ^. _3 - ssh = course ^. _4 - link cid = CourseR tid ssh csh $ CUserR cid - protoCell = listCell (Map.toList users) $ \(userId, (User{..}, mPseudo)) -> - anchorCellM (link <$> encrypt userId) $ case mPseudo of - Nothing -> nameWidget userDisplayName userSurname - Just p -> [whamlet|^{nameWidget userDisplayName userSurname} (#{review _PseudonymText p})|] - in protoCell & cellAttrs <>~ [("class", "list--inline list--comma-separated")] +colSubmittors = sortable (Just "submittors") (i18nCell MsgSubmissionUsers) $ \DBRow{ dbrOutput=(_, Entity _ Sheet{..}, course, _, _, users, _, hasAccess) } -> + let + csh = course ^. _2 + tid = course ^. _3 + ssh = course ^. _4 + link cid = CourseR tid ssh csh $ CUserR cid + protoCell = listCell (Map.toList users) $ \(userId, (User{..}, mPseudo, _)) -> + anchorCellCM $cacheIdentHere (link <$> encrypt userId) $ case mPseudo of + Nothing -> nameWidget userDisplayName userSurname + Just p -> [whamlet|^{nameWidget userDisplayName userSurname} (#{review _PseudonymText p})|] + in if | hasAccess -> protoCell & cellAttrs <>~ [("class", "list--inline list--comma-separated")] + | otherwise -> mempty colSMatrikel :: IsDBTable m a => Colonnade Sortable CorrectionTableData (DBCell m a) -colSMatrikel = sortable Nothing (i18nCell MsgMatrikelNr) $ \DBRow{ dbrOutput=(_, _, _, _, _, users) } -> let - protoCell = listCell (Map.toList users) $ \(userId, (User{..}, _)) -> anchorCellM (AdminUserR <$> encrypt userId) (fromMaybe mempty userMatrikelnummer) - in protoCell & cellAttrs <>~ [("class", "list--inline list--comma-separated")] +colSMatrikel = sortable (Just "submittors-matriculation") (i18nCell MsgMatrikelNr) $ \DBRow{ dbrOutput=(_, Entity _ Sheet{..}, (_, csh, tid, ssh), _, _, users, _, hasAccess) } -> + let protoCell = listCell (Map.toList $ Map.mapMaybe (\x@(User{..}, _, _) -> (x,) <$> assertM (not . null) userMatrikelnummer) users) $ \(userId, ((User{..}, _, _), matr)) -> anchorCellCM $cacheIdentHere (CourseR tid ssh csh . CUserR <$> encrypt userId) matr + in if | hasAccess -> protoCell & cellAttrs <>~ [("class", "list--inline list--comma-separated")] + | otherwise -> mempty + +colSGroups :: IsDBTable m a => Colonnade Sortable CorrectionTableData (DBCell m a) +colSGroups = sortable (Just "submittors-group") (i18nCell MsgSubmissionGroup) $ \DBRow{ dbrOutput=(_, Entity _ Sheet{..}, _, _, _, users, _, hasAccess) } -> + let protoCell = listCell (nubOn (view _2) . Map.toList $ Map.mapMaybe (view _3) users) $ \(_, sGroup) -> cell $ toWidget sGroup + in if | hasAccess + , is _RegisteredGroups sheetGrouping + -> protoCell & cellAttrs <>~ [("class", "list--inline list--comma-separated")] + | otherwise + -> mempty colRating :: forall m a. IsDBTable m (a, SheetTypeSummary) => Colonnade Sortable CorrectionTableData (DBCell m (a, SheetTypeSummary)) -colRating = sortable (Just "rating") (i18nCell MsgRating) $ \DBRow{ dbrOutput=(Entity subId sub@Submission{..}, Entity _ Sheet{..}, course, _, _, _) } -> +colRating = sortable (Just "rating") (i18nCell MsgRating) $ \DBRow{ dbrOutput=(Entity subId sub@Submission{..}, Entity _ Sheet{..}, course, _, _, _, _, _) } -> let csh = course ^. _2 tid = course ^. _3 ssh = course ^. _4 @@ -171,7 +192,7 @@ colRating = sortable (Just "rating") (i18nCell MsgRating) $ \DBRow{ dbrOutput=(E return $ CSubmissionR tid ssh csh sheetName cid CorrectionR mTuple mA mB = (,) <$> mA <*> mB -- Hamlet does not support enough haskell-syntax for this in mconcat - [ anchorCellM mkRoute $(widgetFile "widgets/rating/rating") + [ anchorCellCM $cacheIdentHere mkRoute $(widgetFile "widgets/rating/rating") , writerCell $ do let summary :: SheetTypeSummary @@ -180,48 +201,48 @@ colRating = sortable (Just "rating") (i18nCell MsgRating) $ \DBRow{ dbrOutput=(E ] colAssigned :: IsDBTable m a => Colonnade Sortable CorrectionTableData (DBCell m a) -colAssigned = sortable (Just "assignedtime") (i18nCell MsgAssignedTime) $ \DBRow{ dbrOutput=(Entity _subId Submission{..}, _sheet, _course, _, _, _) } -> +colAssigned = sortable (Just "assignedtime") (i18nCell MsgAssignedTime) $ \DBRow{ dbrOutput=(Entity _subId Submission{..}, _sheet, _course, _, _, _, _, _) } -> maybe mempty dateTimeCell submissionRatingAssigned colRated :: IsDBTable m a => Colonnade Sortable CorrectionTableData (DBCell m a) -colRated = sortable (Just "ratingtime") (i18nCell MsgRatingTime) $ \DBRow{ dbrOutput=(Entity _subId Submission{..}, _sheet, _course, _, _, _) } -> +colRated = sortable (Just "ratingtime") (i18nCell MsgRatingTime) $ \DBRow{ dbrOutput=(Entity _subId Submission{..}, _sheet, _course, _, _, _, _, _) } -> maybe mempty dateTimeCell submissionRatingTime colPseudonyms :: IsDBTable m a => Colonnade Sortable CorrectionTableData (DBCell m a) -colPseudonyms = sortable Nothing (i18nCell MsgPseudonyms) $ \DBRow{ dbrOutput=(_, _, _, _, _, users) } -> let - lCell = listCell (catMaybes $ snd . snd <$> Map.toList users) $ \pseudo -> +colPseudonyms = sortable Nothing (i18nCell MsgPseudonyms) $ \DBRow{ dbrOutput=(_, _, _, _, _, users, _, _) } -> let + lCell = listCell (catMaybes $ view (_2 . _2) <$> Map.toList users) $ \pseudo -> cell [whamlet|#{review _PseudonymText pseudo}|] in lCell & cellAttrs <>~ [("class", "list--inline list--comma-separated")] colRatedField :: Colonnade Sortable CorrectionTableData (DBCell _ (FormResult (DBFormResult SubmissionId (Bool, a, b) CorrectionTableData))) colRatedField = sortable Nothing (i18nCell MsgRatingDone) $ formCell id - (\DBRow{ dbrOutput=(Entity subId _, _, _, _, _, _) } -> return subId) - (\DBRow{ dbrOutput=(Entity _ (submissionRatingDone -> done), _, _, _, _, _) } mkUnique -> over (_1.mapped) (_1 .~) . over _2 fvInput <$> mreq checkBoxField (fsUniq mkUnique "rated") (Just done)) + (\DBRow{ dbrOutput=(Entity subId _, _, _, _, _, _, _, _) } -> return subId) + (\DBRow{ dbrOutput=(Entity _ (submissionRatingDone -> done), _, _, _, _, _, _, _) } mkUnique -> over (_1.mapped) (_1 .~) . over _2 fvInput <$> mreq checkBoxField (fsUniq mkUnique "rated") (Just done)) colPointsField :: Colonnade Sortable CorrectionTableData (DBCell _ (FormResult (DBFormResult SubmissionId (a, Maybe Points, b) CorrectionTableData))) colPointsField = sortable (Just "rating") (i18nCell MsgColumnRatingPoints) $ formCell id - (\DBRow{ dbrOutput=(Entity subId _, _, _, _, _, _) } -> return subId) - (\DBRow{ dbrOutput=(Entity _ Submission{..}, Entity _ Sheet{..}, _, _, _, _) } mkUnique -> case sheetType of + (\DBRow{ dbrOutput=(Entity subId _, _, _, _, _, _, _, _) } -> return subId) + (\DBRow{ dbrOutput=(Entity _ Submission{..}, Entity _ Sheet{..}, _, _, _, _, _, _) } mkUnique -> case sheetType of NotGraded -> over (_1.mapped) (_2 .~) <$> pure (FormSuccess Nothing, mempty) _other -> over (_1.mapped) (_2 .~) . over _2 fvInput <$> mopt (pointsFieldMax $ preview (_grading . _maxPoints) sheetType) (fsUniq mkUnique "points") (Just submissionRatingPoints) ) colMaxPointsField :: Colonnade Sortable CorrectionTableData (DBCell _ (FormResult (DBFormResult SubmissionId (a, Maybe Points, b) CorrectionTableData))) -colMaxPointsField = sortable (Just "sheet-type") (i18nCell MsgSheetType) $ i18nCell . (\DBRow{ dbrOutput=(_, Entity _ Sheet{sheetType}, _, _, _, _) } -> sheetType) +colMaxPointsField = sortable (Just "sheet-type") (i18nCell MsgSheetType) $ i18nCell . (\DBRow{ dbrOutput=(_, Entity _ Sheet{sheetType}, _, _, _, _, _, _) } -> sheetType) colCommentField :: Colonnade Sortable CorrectionTableData (DBCell _ (FormResult (DBFormResult SubmissionId (a, b, Maybe Text) CorrectionTableData))) colCommentField = sortable (Just "comment") (i18nCell MsgRatingComment) $ fmap (cellAttrs <>~ [("style","width:60%")]) $ formCell id - (\DBRow{ dbrOutput=(Entity subId _, _, _, _, _, _) } -> return subId) - (\DBRow{ dbrOutput=(Entity _ Submission{..}, _, _, _, _, _) } mkUnique -> over (_1.mapped) ((_3 .~) . assertM (not . null) . fmap (Text.strip . unTextarea)) . over _2 fvInput <$> mopt textareaField (fsUniq mkUnique "comment") (Just $ Textarea <$> submissionRatingComment)) + (\DBRow{ dbrOutput=(Entity subId _, _, _, _, _, _, _, _) } -> return subId) + (\DBRow{ dbrOutput=(Entity _ Submission{..}, _, _, _, _, _, _, _) } mkUnique -> over (_1.mapped) ((_3 .~) . assertM (not . null) . fmap (Text.strip . unTextarea)) . over _2 fvInput <$> mopt textareaField (fsUniq mkUnique "comment") (Just $ Textarea <$> submissionRatingComment)) colLastEdit :: IsDBTable m a => Colonnade Sortable CorrectionTableData (DBCell m a) colLastEdit = sortable (Just "last-edit") (i18nCell MsgLastEdit) $ - \DBRow{ dbrOutput=(_, _, _, _, mbLastEdit, _) } -> maybe mempty dateTimeCell mbLastEdit + \DBRow{ dbrOutput=(_, _, _, _, mbLastEdit, _, _, _) } -> maybe mempty dateTimeCell mbLastEdit makeCorrectionsTable :: ( IsDBTable m x, ToSortable h, Functor h ) - => CorrectionTableWhere -> Colonnade h CorrectionTableData (DBCell m x) -> _ -> PSValidator m x -> _ -> DBParams m x -> DB (DBResult m x) -makeCorrectionsTable whereClause dbtColonnade dbtFilterUI psValidator dbtProj' dbtParams = do + => CorrectionTableWhere -> Colonnade h CorrectionTableData (DBCell m x) -> _ -> PSValidator m x -> DBParams m x -> DB (DBResult m x) +makeCorrectionsTable whereClause dbtColonnade dbtFilterUI psValidator dbtParams = do let dbtSQLQuery :: CorrectionTableExpr -> E.SqlQuery _ dbtSQLQuery = correctionsTableQuery whereClause (\((course `E.InnerJoin` sheet `E.InnerJoin` submission) `E.LeftOuterJoin` corrector) -> @@ -232,18 +253,27 @@ makeCorrectionsTable whereClause dbtColonnade dbtFilterUI psValidator dbtProj' d ) in (submission, sheet, crse, corrector, lastEditQuery submission) ) - dbtProj :: DBRow _ -> MaybeT (ReaderT SqlBackend (HandlerFor UniWorX)) CorrectionTableData - dbtProj = traverse $ \(submission@(Entity sId _), sheet@(Entity shId _), (E.Value courseName, E.Value courseShorthand, E.Value courseTerm, E.Value courseSchool), mCorrector, E.Value mbLastEdit) -> do - submittors <- lift . E.select . E.from $ \((submissionUser `E.InnerJoin` user) `E.LeftOuterJoin` pseudonym) -> do + dbtProj :: DBRow _ -> DB CorrectionTableData + dbtProj = traverse $ \(submission@(Entity sId _), sheet@(Entity shId Sheet{..}), (E.Value courseName, E.Value courseShorthand, E.Value courseTerm, E.Value courseSchool), mCorrector, E.Value mbLastEdit) -> do + submittors <- E.select . E.from $ \((submissionUser `E.InnerJoin` user) `E.LeftOuterJoin` pseudonym) -> do E.on $ pseudonym E.?. SheetPseudonymUser E.==. E.just (user E.^. UserId) E.&&. pseudonym E.?. SheetPseudonymSheet E.==. E.just (E.val shId) E.on $ submissionUser E.^. SubmissionUserUser E.==. user E.^. UserId E.where_ $ submissionUser E.^. SubmissionUserSubmission E.==. E.val sId E.orderBy [E.asc $ user E.^. UserSurname, E.asc $ user E.^. UserDisplayName] - return (user, pseudonym E.?. SheetPseudonymPseudonym) + let submissionGroup' = E.subSelectMaybe . E.from $ \(submissionGroup `E.InnerJoin` submissionGroupUser) -> do + E.on $ submissionGroup E.^. SubmissionGroupId E.==. submissionGroupUser E.^. SubmissionGroupUserSubmissionGroup + E.where_ $ submissionGroup E.^. SubmissionGroupCourse E.==. E.val sheetCourse + E.where_ $ submissionGroupUser E.^. SubmissionGroupUserUser E.==. user E.^. UserId + return . E.just $ submissionGroup E.^. SubmissionGroupName + return (user, pseudonym E.?. SheetPseudonymPseudonym, submissionGroup') let - submittorMap = List.foldr (\(Entity userId user, E.Value pseudo) -> Map.insert userId (user, pseudo)) Map.empty submittors - dbtProj' (submission, sheet, (courseName, courseShorthand, courseTerm, courseSchool), mCorrector, mbLastEdit, submittorMap) + submittorMap = List.foldr (\(Entity userId user, E.Value pseudo, E.Value sGroup) -> Map.insert userId (user, pseudo, sGroup)) Map.empty submittors + nonAnonymousAccess <- or2M + (return $ not sheetAnonymousCorrection) + (hasReadAccessTo $ CourseR courseTerm courseSchool courseShorthand CCorrectionsR) + cid <- encrypt sId + return (submission, sheet, (courseName, courseShorthand, courseTerm, courseSchool), mCorrector, mbLastEdit, submittorMap, cid, nonAnonymousAccess) dbTable psValidator DBTable { dbtSQLQuery , dbtRowKey = \((_ `E.InnerJoin` _ `E.InnerJoin` submission) `E.LeftOuterJoin` _ :: CorrectionTableExpr) -> submission E.^. SubmissionId @@ -285,13 +315,13 @@ makeCorrectionsTable whereClause dbtColonnade dbtFilterUI psValidator dbtProj' d , SortColumn $ \((_ `E.InnerJoin` _ `E.InnerJoin` submission) `E.LeftOuterJoin` _) -> submission E.^. SubmissionRatingAssigned ) , ( "submittors" - , SortColumn $ \((_ `E.InnerJoin` _ `E.InnerJoin` submission) `E.LeftOuterJoin` _) -> - E.subSelectUnsafe . E.from $ \(submissionUser `E.InnerJoin` user) -> do - E.on $ submissionUser E.^. SubmissionUserUser E.==. user E.^. UserId - E.where_ $ submissionUser E.^. SubmissionUserSubmission E.==. submission E.^. SubmissionId - E.orderBy [E.asc $ user E.^. UserSurname, E.asc $ user E.^. UserDisplayName] - E.limit 1 - return (user E.^. UserSurname) + , SortProjected . comparing $ \DBRow{ dbrOutput = (_, _, _, _, _, submittors, _, hasAccess) } -> guardOn @Maybe hasAccess . fmap ((userSurname &&& userDisplayName) . view _1) $ Map.elems submittors + ) + , ( "submittors-matriculation" + , SortProjected . comparing $ \DBRow{ dbrOutput = (_, _, _, _, _, submittors, _, hasAccess) } -> guardOn @Maybe hasAccess . fmap (view $ _1 . _userMatrikelnummer) $ Map.elems submittors + ) + , ( "submittors-group" + , SortProjected . comparing $ \DBRow{ dbrOutput = (_, _, _, _, _, submittors, _, hasAccess) } -> guardOn @Maybe hasAccess . fmap (view _3) $ Map.elems submittors ) , ( "comment" -- sorting by comment specifically requested by correctors to easily see submissions to be done , SortColumn $ \((_ `E.InnerJoin` _ `E.InnerJoin` submission) `E.LeftOuterJoin` _) -> submission E.^. SubmissionRatingComment @@ -299,6 +329,9 @@ makeCorrectionsTable whereClause dbtColonnade dbtFilterUI psValidator dbtProj' d , ( "last-edit" , SortColumn $ \((_course `E.InnerJoin` _sheet `E.InnerJoin` submission) `E.LeftOuterJoin` _corrector) -> lastEditQuery submission ) + , ( "submission" + , SortProjected . comparing $ toPathPiece . view (_dbrOutput . _7) + ) ] , dbtFilter = Map.fromList [ ( "term" @@ -368,6 +401,13 @@ makeCorrectionsTable whereClause dbtColonnade dbtFilterUI psValidator dbtProj' d E.where_ $ (\f -> f user $ Set.singleton needle) $ E.mkContainsFilter (E.^. UserMatrikelnummer) ) + , ( "submission-group" + , FilterColumn $ E.mkExistsFilter $ \table needle -> E.from $ \(submissionGroup `E.InnerJoin` submissionGroupUser) -> do + E.on $ submissionGroup E.^. SubmissionGroupId E.==. submissionGroupUser E.^. SubmissionGroupUserSubmissionGroup + E.where_ $ queryCourse table E.^. CourseId E.==. submissionGroup E.^. SubmissionGroupCourse + E.where_ $ (\f -> f submissionGroup $ Set.singleton needle) $ + E.mkContainsFilter (E.^. SubmissionGroupName) + ) , ( "rating-visible" , FilterColumn $ \((_ `E.InnerJoin` _ `E.InnerJoin` submission) `E.LeftOuterJoin` _ :: CorrectionTableExpr) criterion -> case getLast (criterion :: Last Bool) of Nothing -> E.val True :: E.SqlExpr (E.Value Bool) @@ -384,6 +424,12 @@ makeCorrectionsTable whereClause dbtColonnade dbtFilterUI psValidator dbtProj' d Nothing -> E.val True :: E.SqlExpr (E.Value Bool) Just needle -> E.maybe (E.val False :: E.SqlExpr (E.Value Bool)) (E.isInfixOf $ E.val needle) (submission E.^. SubmissionRatingComment) ) + , ( "submission" + , FilterProjected $ \(DBRow{..} :: CorrectionTableData) (criteria :: Set Text) -> + let cid = map CI.mk . unpack . toPathPiece $ dbrOutput ^. _7 + criteria' = map CI.mk . unpack <$> Set.toList criteria + in any (\c -> c `isInfixOf` cid) criteria' + ) ] , dbtFilterUI = fromMaybe mempty dbtFilterUI , dbtStyle = def { dbsFilterLayout = maybe (\_ _ _ -> id) (\_ -> defaultDBSFilterLayout) dbtFilterUI } @@ -405,13 +451,21 @@ instance Finite ActionCorrections nullaryPathPiece ''ActionCorrections $ camelToPathPiece' 1 embedRenderMessage ''UniWorX ''ActionCorrections id -data ActionCorrectionsData = CorrDownloadData +data ActionCorrectionsData = CorrDownloadData SubmissionDownloadAnonymous | CorrSetCorrectorData (Maybe UserId) | CorrAutoSetCorrectorData SheetId | CorrDeleteData correctionsR :: _ -> _ -> _ -> _ -> Map ActionCorrections (AForm (HandlerFor UniWorX) ActionCorrectionsData) -> Handler TypedContent correctionsR whereClause displayColumns dbtFilterUI psValidator actions = do + (table, statistics) <- correctionsR' whereClause displayColumns dbtFilterUI psValidator actions + + fmap toTypedContent . defaultLayout $ do + setTitleI MsgCourseCorrectionsTitle + $(widgetFile "corrections") + +correctionsR' :: _ -> _ -> _ -> _ -> Map ActionCorrections (AForm (HandlerFor UniWorX) ActionCorrectionsData) -> Handler (Widget, SheetTypeSummary) +correctionsR' whereClause displayColumns dbtFilterUI psValidator actions = do currentRoute <- fromMaybe (error "correctionsR called from 404-handler") <$> getCurrentRoute -- This should never be called from a 404 handler postDeleteR $ \drRecords -> (submissionDeleteRoute drRecords) @@ -420,7 +474,7 @@ correctionsR whereClause displayColumns dbtFilterUI psValidator actions = do } ((actionRes', statistics), table) <- runDB $ - makeCorrectionsTable whereClause displayColumns dbtFilterUI psValidator return DBParamsForm + makeCorrectionsTable whereClause displayColumns dbtFilterUI psValidator DBParamsForm { dbParamsFormMethod = POST , dbParamsFormAction = Just $ SomeRoute currentRoute , dbParamsFormAttrs = [] @@ -445,14 +499,12 @@ correctionsR whereClause displayColumns dbtFilterUI psValidator actions = do & mapped._1 %~ fromMaybe (error "By consctruction the form should always return an action") . getLast auditAllSubEdit = mapM_ $ \sId -> getJust sId >>= \sub -> audit $ TransactionSubmissionEdit sId $ sub ^. _submissionSheet - case actionRes of - FormFailure errs -> mapM_ (addMessage Warning . toHtml) errs - FormMissing -> return () - FormSuccess (CorrDownloadData, subs) -> do + formResult actionRes $ \case + (CorrDownloadData nonAnonymous, subs) -> do ids <- Set.fromList <$> forM (Set.toList subs) decrypt -- Set is not traversable addHeader "Content-Disposition" [st|attachment; filename="corrections.zip"|] - sendResponse =<< submissionMultiArchive ids - FormSuccess (CorrSetCorrectorData (Just uid), subs') -> do + sendResponse =<< submissionMultiArchive nonAnonymous ids + (CorrSetCorrectorData (Just uid), subs') -> do subs <- mapM decrypt $ Set.toList subs' now <- liftIO getCurrentTime runDB $ do @@ -485,7 +537,7 @@ correctionsR whereClause displayColumns dbtFilterUI psValidator actions = do return (E.countRows :: E.SqlExpr (E.Value Int64)) when (selfCorrectors > 0) $ addMessageI Warning $ MsgSelfCorrectors selfCorrectors redirect currentRoute - FormSuccess (CorrSetCorrectorData Nothing, subs') -> do -- delete corrections + (CorrSetCorrectorData Nothing, subs') -> do -- delete corrections subs <- mapM decrypt $ Set.toList subs' runDB $ do num <- updateWhereCount [SubmissionId <-. subs] @@ -498,7 +550,7 @@ correctionsR whereClause displayColumns dbtFilterUI psValidator actions = do addMessageI Success $ MsgRemovedCorrections num auditAllSubEdit subs redirect currentRoute - FormSuccess (CorrAutoSetCorrectorData shid, subs') -> do + (CorrAutoSetCorrectorData shid, subs') -> do subs <- mapM decrypt $ Set.toList subs' let assignExceptions :: AssignSubmissionException -> Handler () @@ -535,16 +587,14 @@ correctionsR whereClause displayColumns dbtFilterUI psValidator actions = do unassigned' <- forM (Set.toList stillUnassigned) $ \sid -> encrypt sid :: DB CryptoFileNameSubmission addMessage Warning =<< withUrlRenderer ($(ihamletFile "templates/messages/submissionsNotAssignedAuto.hamlet") mr) redirect currentRoute - FormSuccess (CorrDeleteData, subs) -> do + (CorrDeleteData, subs) -> do subs' <- Set.fromList <$> forM (Set.toList subs) decrypt -- Set is not traversable getDeleteR (submissionDeleteRoute subs') { drAbort = SomeRoute currentRoute , drSuccess = SomeRoute currentRoute } - fmap toTypedContent . defaultLayout $ do - setTitleI MsgCourseCorrectionsTitle - $(widgetFile "corrections") + return (table, statistics) where authorizedToAssign :: SubmissionId -> DB Bool @@ -559,11 +609,22 @@ correctionsR whereClause displayColumns dbtFilterUI psValidator actions = do let route = CSubmissionR tid ssh csh shn cID SubAssignR (== Authorized) <$> evalAccessDB route True +restrictAnonymous :: PSValidator m x -> PSValidator m x +restrictAnonymous = restrictFilter (\k _ -> k /= "user-matriclenumber") + . restrictFilter (\k _ -> k /= "user-name-email") + . restrictFilter (\k _ -> k /= "submission-group") + . restrictSorting (\k _ -> k /= "last-edit") + +restrictCorrector :: PSValidator m x -> PSValidator m x +restrictCorrector = restrictFilter (\k _ -> k /= "corrector") + . restrictFilter (\k _ -> k /= "corrector-name-email") + . restrictSorting (\k _ -> k /= "corrector") + type ActionCorrections' = (ActionCorrections, AForm (HandlerFor UniWorX) ActionCorrectionsData) downloadAction, deleteAction :: ActionCorrections' downloadAction = ( CorrDownload - , pure CorrDownloadData + , CorrDownloadData <$> apopt (selectField optionsFinite) (fslI MsgCorrDownloadAnonymous & setTooltip MsgCorrDownloadAnonymousTip) (Just SubmissionDownloadAnonymous) ) deleteAction = ( CorrDelete , pure CorrDeleteData @@ -601,11 +662,13 @@ postCorrectionsR = do let whereClause = ratedBy uid colonnade = mconcat [ colSelect - , dbRow -- very useful, since correction statistics are still missing. , colSchool , colTerm , colCourse , colSheet + , colSMatrikel + , colSubmittors + , colSGroups , colPseudonyms , colSubmissionLink , colAssigned @@ -618,6 +681,7 @@ postCorrectionsR = do , prismAForm (singletonFilter "school" ) mPrev $ aopt (lift `hoistField` selectField schoolOptions) (fslI MsgCourseSchool) , Map.singleton "sheet-search" . maybeToList <$> aopt (lift `hoistField` textField) (fslI MsgSheet) (Just <$> listToMaybe =<< ((Map.lookup "sheet-search" =<< mPrev) <|> (Map.lookup "sheet" =<< mPrev))) , prismAForm (singletonFilter "israted" . maybePrism _PathPiece) mPrev $ aopt (boolField . Just $ SomeMessage MsgBoolIrrelevant) (fslI MsgRatingTime) + , prismAForm (singletonFilter "submission") mPrev $ aopt (lift `hoistField` textField) (fslI MsgSubmission) ] courseOptions = runDB $ do courses <- selectList [] [Asc CourseShorthand] >>= filterM (\(Entity _ Course{..}) -> (== Authorized) <$> evalAccessCorrector courseTerm courseSchool courseShorthand) @@ -630,8 +694,8 @@ postCorrectionsR = do optionsPairs $ map (id &&& id) $ nub $ map (CI.original . unSchoolKey . courseSchool . entityVal) courses psValidator = def - & restrictFilter (\name _ -> name /= "corrector") -- We need to be careful to restrict allowed sorting/filter to not expose sensitive information - & restrictSorting (\name _ -> name /= "corrector") + & restrictCorrector + & restrictAnonymous & defaultSorting [SortAscBy "israted", SortDescBy "ratingTime", SortAscBy "assignedtime" ] -- & defaultFilter (Map.fromList [("israted",[toPathPiece False])]) -- DEPENDS ON ISSUE #371 UNCOMMENT THEN correctionsR whereClause colonnade filterUI psValidator $ Map.fromList @@ -645,10 +709,10 @@ postCCorrectionsR tid ssh csh = do let whereClause = courseIs cid colonnade = mconcat -- should match getSSubsR for consistent UX [ colSelect - , dbRow , colSheet , colSMatrikel , colSubmittors + , colSGroups , colSubmissionLink , colLastEdit , colRating @@ -664,6 +728,8 @@ postCCorrectionsR tid ssh csh = do , prismAForm (singletonFilter "corrector-name-email") mPrev $ aopt textField (fslI MsgCorrector) , prismAForm (singletonFilter "isassigned" . maybePrism _PathPiece) mPrev $ aopt (boolField . Just $ SomeMessage MsgBoolIrrelevant) (fslI MsgHasCorrector) , prismAForm (singletonFilter "israted" . maybePrism _PathPiece) mPrev $ aopt (boolField . Just $ SomeMessage MsgBoolIrrelevant) (fslI MsgRatingTime) + , prismAForm (singletonFilter "submission-group") mPrev $ aopt textField (fslI MsgSubmissionGroup) + , prismAForm (singletonFilter "submission") mPrev $ aopt (lift `hoistField` textField) (fslI MsgSubmission) ] psValidator = def & defaultPagesize PagesizeAll -- Assisstant always want to see them all at once anyway correctionsR whereClause colonnade filterUI psValidator $ Map.fromList @@ -679,7 +745,6 @@ postSSubsR tid ssh csh shn = do let whereClause = sheetIs shid colonnade = mconcat -- should match getCCorrectionsR for consistent UX [ colSelect - , dbRow , colSMatrikel , colSubmittors , colSubmissionLink @@ -695,6 +760,8 @@ postSSubsR tid ssh csh shn = do , prismAForm (singletonFilter "corrector-name-email") mPrev $ aopt textField (fslI MsgCorrector) , prismAForm (singletonFilter "isassigned" . maybePrism _PathPiece) mPrev $ aopt (boolField . Just $ SomeMessage MsgBoolIrrelevant) (fslI MsgHasCorrector) , prismAForm (singletonFilter "israted" . maybePrism _PathPiece) mPrev $ aopt (boolField . Just $ SomeMessage MsgBoolIrrelevant) (fslI MsgRatingTime) + , prismAForm (singletonFilter "submission-group") mPrev $ aopt textField (fslI MsgSubmissionGroup) + , prismAForm (singletonFilter "submission") mPrev $ aopt (lift `hoistField` textField) (fslI MsgSubmission) -- "pseudonym" TODO DB only stores Word24 ] psValidator = def & defaultPagesize PagesizeAll -- Assisstant always want to see them all at once anyway @@ -1025,6 +1092,9 @@ postCorrectionsGradeR = do , colTerm , colCourse , colSheet + , colSMatrikel + , colSubmittors + , colSGroups , colPseudonyms , colSubmissionLink , colRated @@ -1053,37 +1123,33 @@ postCorrectionsGradeR = do courses <- selectList [] [Asc CourseSchool] >>= filterM (\(Entity _ Course{..}) -> (== Authorized) <$> evalAccessCorrector courseTerm courseSchool courseShorthand) optionsPairs $ map (id &&& id) $ nub $ map (CI.original . unSchoolKey . courseSchool . entityVal) courses psValidator = def + & restrictAnonymous + & restrictCorrector & defaultSorting [SortDescBy "ratingtime"] :: PSValidator (MForm (HandlerFor UniWorX)) (FormResult (DBFormResult SubmissionId (Bool, Maybe Points, Maybe Text) CorrectionTableData)) - unFormResult = getDBFormResult $ \DBRow{ dbrOutput = (Entity _ sub@Submission{..}, _, _, _, _, _) } -> (submissionRatingDone sub, submissionRatingPoints, submissionRatingComment) - dbtProj' i@(Entity subId _, Entity _ Sheet{ sheetName = shn }, (_, csh, tid, ssh), _, _, _) = do - cID <- encrypt subId - void . assertM (== Authorized) . lift $ evalAccessDB (CSubmissionR tid ssh csh shn cID CorrectionR) True - return i + unFormResult = getDBFormResult $ \DBRow{ dbrOutput = (Entity _ sub@Submission{..}, _, _, _, _, _, _, _) } -> (submissionRatingDone sub, submissionRatingPoints, submissionRatingComment) - (fmap unFormResult -> tableRes, table) <- runDB $ makeCorrectionsTable whereClause displayColumns filterUI psValidator dbtProj' $ def + (fmap unFormResult -> tableRes, table) <- runDB $ makeCorrectionsTable whereClause displayColumns filterUI psValidator $ def { dbParamsFormAction = Just $ SomeRoute CorrectionsGradeR } - case tableRes of - FormMissing -> return () - FormFailure errs -> forM_ errs $ addMessage Error . toHtml - FormSuccess resMap -> do - now <- liftIO getCurrentTime - subs <- fmap catMaybes . runDB . forM (Map.toList resMap) $ \(subId, (rated, mPoints, mComment)) -> do - s@Submission{..} <- get404 subId - if - | submissionRatingPoints /= mPoints || submissionRatingComment /= mComment || rated /= submissionRatingDone s - -> do audit $ TransactionSubmissionEdit subId $ s ^. _submissionSheet - Just subId <$ update subId [ SubmissionRatingPoints =. mPoints - , SubmissionRatingComment =. mComment - , SubmissionRatingBy =. Just uid - , SubmissionRatingTime =. now <$ guard rated - ] - | otherwise -> return Nothing - subs' <- traverse (\x -> (,) <$> encrypt x <*> encrypt x) subs :: Handler [(CryptoFileNameSubmission, CryptoUUIDSubmission)] - let trigger = [whamlet|_{MsgCorrectionsUploaded (genericLength subs')}|] - content = Right $(widgetFile "messages/correctionsUploaded") - unless (null subs') $ addMessageModal Success trigger content + formResult tableRes $ \resMap -> do + now <- liftIO getCurrentTime + subs <- fmap catMaybes . runDB . forM (Map.toList resMap) $ \(subId, (rated, mPoints, mComment)) -> do + s@Submission{..} <- get404 subId + if + | submissionRatingPoints /= mPoints || submissionRatingComment /= mComment || rated /= submissionRatingDone s + -> do audit $ TransactionSubmissionEdit subId $ s ^. _submissionSheet + Just subId <$ update subId [ SubmissionRatingPoints =. mPoints + , SubmissionRatingComment =. mComment + , SubmissionRatingBy =. Just uid + , SubmissionRatingTime =. now <$ guard rated + ] + | otherwise -> return Nothing + subs' <- traverse (\x -> (,) <$> encrypt x <*> encrypt x) subs :: Handler [(CryptoFileNameSubmission, CryptoUUIDSubmission)] + let trigger = [whamlet|_{MsgCorrectionsUploaded (genericLength subs')}|] + content = Right $(widgetFile "messages/correctionsUploaded") + unless (null subs') $ addMessageModal Success trigger content + redirect CorrectionsGradeR siteLayoutMsg MsgCorrectionsGrade $ do setTitleI MsgCorrectionsGrade @@ -1099,9 +1165,6 @@ embedRenderMessage ''UniWorX ''ButtonSubmissionsAssign id instance Button UniWorX ButtonSubmissionsAssign where btnClasses BtnSubmissionsAssign = [BCIsButton, BCPrimary] --- | DEPRECATED use CorrectorInfo instead. Gather info about corrector assignment per sheet -data SubAssignInfo = SubAssignInfo { saiName :: SheetName, saiSubmissionNr, saiCorrectorNr, saiUnassignedNr :: Int } - getCAssignR, postCAssignR :: TermId -> SchoolId -> CourseShorthand -> Handler Html getCAssignR = postCAssignR postCAssignR tid ssh csh = do @@ -1140,7 +1203,7 @@ assignHandler tid ssh csh cid assignSids = do -- gather data (orderedSheetNames, assignSheetNames, nrParticipants, groupsPossible, infoMap, correctorMap, assignment) <- runDB $ do -- cid <- getKeyBy404 $ TermSchoolCourseShort tid ssh csh - nrParticipants <- count [CourseParticipantCourse ==. cid] + nrParticipants <- count [CourseParticipantCourse ==. cid, CourseParticipantState ==. CourseParticipantActive ] sheetList <- selectList [SheetCourse ==. cid] [Desc SheetActiveTo, Desc SheetActiveFrom] let orderedSheetNames = fmap (\(Entity _ Sheet{sheetName}) -> sheetName) sheetList diff --git a/src/Handler/Course/Application/List.hs b/src/Handler/Course/Application/List.hs index a59370ac0..1c7cdf6c9 100644 --- a/src/Handler/Course/Application/List.hs +++ b/src/Handler/Course/Application/List.hs @@ -257,6 +257,7 @@ getCApplicationsR, postCApplicationsR :: TermId -> SchoolId -> CourseShorthand - getCApplicationsR = postCApplicationsR postCApplicationsR tid ssh csh = do (table, allocationsBounds, mayAccept) <- runDB $ do + now <- liftIO getCurrentTime Entity cid Course{..} <- getBy404 $ TermSchoolCourseShort tid ssh csh csvName <- getMessageRender <*> pure (MsgCourseApplicationsTableCsvName tid ssh csh) @@ -264,13 +265,13 @@ postCApplicationsR tid ssh csh = do allocationLink :: Allocation -> SomeRoute UniWorX allocationLink Allocation{..} = SomeRoute $ AllocationR allocationTerm allocationSchool allocationShorthand AShowR - participantLink :: MonadCrypto m => UserId -> m (SomeRoute UniWorX) - participantLink uid = do + participantLink :: (MonadHandler m, HandlerSite m ~ UniWorX) => UserId -> m (SomeRoute UniWorX) + participantLink uid = liftHandler $ do cID <- encrypt uid return . SomeRoute . CourseR tid ssh csh $ CUserR cID - applicationLink :: MonadCrypto m => CourseApplicationId -> m (SomeRoute UniWorX) - applicationLink appId = do + applicationLink :: (MonadHandler m, HandlerSite m ~ UniWorX) => CourseApplicationId -> m (SomeRoute UniWorX) + applicationLink appId = liftHandler $ do cID <- encrypt appId return . SomeRoute $ CApplicationR tid ssh csh cID CAEditR @@ -288,6 +289,7 @@ postCApplicationsR tid ssh csh = do lift $ do E.on $ E.just (user E.^. UserId) E.==. courseParticipant E.?. CourseParticipantUser E.&&. courseParticipant E.?. CourseParticipantCourse E.==. E.just (E.val cid) + E.&&. courseParticipant E.?. CourseParticipantState E.==. E.just (E.val CourseParticipantActive) E.on $ studyDegree E.?. StudyDegreeId E.==. studyFeatures E.?. StudyFeaturesDegree E.on $ studyTerms E.?. StudyTermsId E.==. studyFeatures E.?. StudyFeaturesField E.on $ studyFeatures E.?. StudyFeaturesId E.==. courseApplication E.^. CourseApplicationField @@ -295,6 +297,8 @@ postCApplicationsR tid ssh csh = do E.on $ user E.^. UserId E.==. courseApplication E.^. CourseApplicationUser E.&&. courseApplication E.^. CourseApplicationCourse E.==. E.val cid + E.where_ $ E.maybe E.true (E.maybe E.false (E.<=. E.val now)) (allocation E.?. AllocationStaffAllocationFrom) + return ( courseApplication , user , hasFiles @@ -305,14 +309,8 @@ postCApplicationsR tid ssh csh = do , E.not_ . E.isNothing $ courseParticipant E.?. CourseParticipantId ) - dbtProj :: DBRow _ -> MaybeT (YesodDB UniWorX) CourseApplicationsTableData - dbtProj = runReaderT $ do - appId <- view $ _dbrOutput . _1 . _entityKey - cID <- encrypt appId - - guardM . hasReadAccessTo $ CApplicationR tid ssh csh cID CAEditR - - asks $ over (_dbrOutput . _3) E.unValue . over (_dbrOutput . _8) E.unValue + dbtProj :: DBRow _ -> DB CourseApplicationsTableData + dbtProj = traverse $ return . over _3 E.unValue . over _8 E.unValue dbtRowKey = view $ queryCourseApplication . to (E.^. CourseApplicationId) @@ -431,7 +429,6 @@ postCApplicationsR tid ssh csh = do CourseApplicationsTableCsvSetCommentData{} -> CourseApplicationsTableCsvSetComment , dbtCsvCoarsenActionClass = const DBCsvActionExisting , dbtCsvExecuteActions = do - now <- liftIO getCurrentTime C.mapM_ $ \case CourseApplicationsTableCsvSetFieldData{..} -> do CourseApplication{..} <- updateGet caCsvActApplication [ CourseApplicationField =. caCsvActField @@ -591,7 +588,7 @@ postCApplicationsR tid ssh csh = do psValidator = def & defaultSorting [SortAscBy "user-name"] - participants <- count [ CourseParticipantCourse ==. cid ] + participants <- count [ CourseParticipantCourse ==. cid, CourseParticipantState ==. CourseParticipantActive ] let remainingCapacity = subtract participants <$> courseCapacity allocationsBounds' <- E.select . E.from $ \(allocation `E.InnerJoin` allocationCourse) -> do @@ -647,7 +644,7 @@ postCApplicationsR tid ssh csh = do formResult acceptRes $ \(invMode, appsSecOrder) -> do runDBJobs $ do Entity cid Course{..} <- getBy404 $ TermSchoolCourseShort tid ssh csh - participants <- count [ CourseParticipantCourse ==. cid ] + participants <- count [ CourseParticipantCourse ==. cid, CourseParticipantState ==. CourseParticipantActive ] let openCapacity = subtract participants <$> courseCapacity applications <- E.select . E.from $ \(user `E.InnerJoin` application) -> do @@ -661,6 +658,7 @@ postCApplicationsR tid ssh csh = do E.where_ . E.not_ . E.exists . E.from $ \participant -> E.where_ $ participant E.^. CourseParticipantCourse E.==. E.val cid E.&&. participant E.^. CourseParticipantUser E.==. user E.^. UserId + E.&&. participant E.^. CourseParticipantState E.==. E.val CourseParticipantActive return (user, application) @@ -684,6 +682,8 @@ postCApplicationsR tid ssh csh = do mapM_ addMessage' <=< execWriterT $ registerUsers cid applicants redirect $ CourseR tid ssh csh CUsersR + let + studyFeaturesWarning = $(i18nWidgetFile "applications-list-info") siteLayoutMsg title $ do setTitleI title diff --git a/src/Handler/Course/Communication.hs b/src/Handler/Course/Communication.hs index a7fb00ac8..1b86fb4fd 100644 --- a/src/Handler/Course/Communication.hs +++ b/src/Handler/Course/Communication.hs @@ -40,6 +40,7 @@ postCCommR tid ssh csh = do , E.from $ \(user `E.InnerJoin` participant) -> do E.on $ user E.^. UserId E.==. participant E.^. CourseParticipantUser E.where_ $ participant E.^. CourseParticipantCourse E.==. E.val cid + E.&&. participant E.^. CourseParticipantState E.==. E.val CourseParticipantActive return user ) , ( RGCourseLecturers diff --git a/src/Handler/Course/Edit.hs b/src/Handler/Course/Edit.hs index 8707c568b..1e83e4393 100644 --- a/src/Handler/Course/Edit.hs +++ b/src/Handler/Course/Edit.hs @@ -55,10 +55,11 @@ data CourseForm = CourseForm data AllocationCourseForm = AllocationCourseForm { acfAllocation :: AllocationId , acfMinCapacity :: Int + , acfDeregisterNoShow :: Bool } courseToForm :: Entity Course -> [Lecturer] -> Map UserEmail (InvitationDBData Lecturer) -> Maybe (Entity AllocationCourse) -> CourseForm -courseToForm (Entity cid Course{..}) lecs lecInvites alloc = CourseForm +courseToForm cEnt@(Entity cid Course{..}) lecs lecInvites alloc = CourseForm { cfCourseId = Just cid , cfName = courseName , cfDesc = courseDescription @@ -69,7 +70,7 @@ courseToForm (Entity cid Course{..}) lecs lecInvites alloc = CourseForm , cfCapacity = courseCapacity , cfSecret = courseRegisterSecret , cfMatFree = courseMaterialFree - , cfAllocation = allocationCourseToForm <$> alloc + , cfAllocation = allocationCourseToForm cEnt <$> alloc , cfAppRequired = courseApplicationsRequired , cfAppInstructions = courseApplicationsInstructions , cfAppInstructionFiles @@ -89,10 +90,11 @@ courseToForm (Entity cid Course{..}) lecs lecInvites alloc = CourseForm return $ courseAppInstructionFile E.^. CourseAppInstructionFileFile -allocationCourseToForm :: Entity AllocationCourse -> AllocationCourseForm -allocationCourseToForm (Entity _ AllocationCourse{..}) = AllocationCourseForm +allocationCourseToForm :: Entity Course -> Entity AllocationCourse -> AllocationCourseForm +allocationCourseToForm (Entity _ Course{..}) (Entity _ AllocationCourse{..}) = AllocationCourseForm { acfAllocation = allocationCourseAllocation , acfMinCapacity = allocationCourseMinCapacity + , acfDeregisterNoShow = courseDeregisterNoShow } makeCourseForm :: (forall p. PathPiece p => p -> Maybe (SomeRoute UniWorX)) -> Maybe CourseForm -> Form CourseForm @@ -251,6 +253,7 @@ makeCourseForm miButtonAction template = identifyForm FIDcourse . validateFormDB in AllocationCourseForm <$> ainp (selectField' Nothing $ return allocationOptions) (fslI MsgCourseAllocation) (fmap acfAllocation $ template >>= cfAllocation) <*> ainp (natFieldI MsgCourseAllocationMinCapacityMustBeNonNegative) (fslI MsgCourseAllocationMinCapacity & setTooltip MsgCourseAllocationMinCapacityTip) (fmap acfMinCapacity $ template >>= cfAllocation) + <*> apopt checkBoxField (fslI MsgCourseDeregisterNoShow & setTooltip MsgCourseDeregisterNoShowTip) ((<|> Just True) . fmap acfDeregisterNoShow $ template >>= cfAllocation) optionalActionW' (bool mforcedJust mpopt mayChange) allocationForm' (fslI MsgCourseAllocationParticipate & setTooltip MsgCourseAllocationParticipateTip) (is _Just . cfAllocation <$> template) @@ -459,6 +462,7 @@ courseEditHandler miButtonAction mbCourseForm = do , courseRegisterFrom = cfRegFrom , courseRegisterTo = cfRegTo , courseDeregisterUntil = cfDeRegUntil + , courseDeregisterNoShow = maybe False acfDeregisterNoShow cfAllocation } whenIsJust insertOkay $ \cid -> do let (invites, adds) = partitionEithers $ cfLecturers res @@ -506,6 +510,7 @@ courseEditHandler miButtonAction mbCourseForm = do , courseRegisterFrom = cfRegFrom , courseRegisterTo = cfRegTo , courseDeregisterUntil = cfDeRegUntil + , courseDeregisterNoShow = maybe False acfDeregisterNoShow cfAllocation } case updOkay of (Just _) -> addMessageI Warning (MsgCourseEditDupShort tid ssh csh) $> False diff --git a/src/Handler/Course/Events/Edit.hs b/src/Handler/Course/Events/Edit.hs index e762b3ae0..5ac391d5d 100644 --- a/src/Handler/Course/Events/Edit.hs +++ b/src/Handler/Course/Events/Edit.hs @@ -24,6 +24,7 @@ postCEvEditR tid ssh csh cID = do , courseEventType = cefType , courseEventRoom = cefRoom , courseEventTime = cefTime + , courseEventNote = cefNote , courseEventLastChanged = now } addMessageI Success MsgCourseEventEdited diff --git a/src/Handler/Course/Events/Form.hs b/src/Handler/Course/Events/Form.hs index bf9d99ca8..3cb291f89 100644 --- a/src/Handler/Course/Events/Form.hs +++ b/src/Handler/Course/Events/Form.hs @@ -15,6 +15,7 @@ data CourseEventForm = CourseEventForm { cefType :: CI Text , cefRoom :: Text , cefTime :: Occurrences + , cefNote :: Maybe Html } courseEventForm :: Maybe CourseEventForm -> Form CourseEventForm @@ -34,15 +35,18 @@ courseEventForm template = identifyForm FIDCourseEvent . renderWForm FormStandar cefType' <- wreq (textField & cfStrip & cfCI & addDatalist courseEventTypes) (fslI MsgCourseEventType & addPlaceholder (mr MsgCourseEventTypePlaceholder)) (cefType <$> template) cefRoom' <- wreq (textField & cfStrip & addDatalist courseEventRooms) (fslI MsgCourseEventRoom) (cefRoom <$> template) cefTime' <- aFormToWForm $ occurrencesAForm ("time" :: Text) (cefTime <$> template) + cefNote' <- wopt htmlField (fslI MsgCourseEventNote) (cefNote <$> template) return $ CourseEventForm <$> cefType' <*> cefRoom' <*> cefTime' + <*> cefNote' courseEventToForm :: CourseEvent -> CourseEventForm courseEventToForm CourseEvent{..} = CourseEventForm { cefType = courseEventType , cefRoom = courseEventRoom , cefTime = courseEventTime + , cefNote = courseEventNote } diff --git a/src/Handler/Course/Events/New.hs b/src/Handler/Course/Events/New.hs index 248856cad..b01f17af5 100644 --- a/src/Handler/Course/Events/New.hs +++ b/src/Handler/Course/Events/New.hs @@ -22,6 +22,7 @@ postCEventsNewR tid ssh csh = do , courseEventType = cefType , courseEventRoom = cefRoom , courseEventTime = cefTime + , courseEventNote = cefNote , courseEventLastChanged = now } encrypt eId :: DB CryptoUUIDCourseEvent diff --git a/src/Handler/Course/LecturerInvite.hs b/src/Handler/Course/LecturerInvite.hs index 0e410a597..53d7156ac 100644 --- a/src/Handler/Course/LecturerInvite.hs +++ b/src/Handler/Course/LecturerInvite.hs @@ -17,6 +17,8 @@ import Data.Aeson hiding (Result(..)) import Text.Hamlet (ihamlet) +import qualified Data.HashSet as HashSet + instance IsInvitableJunction Lecturer where type InvitationFor Lecturer = Course @@ -65,7 +67,7 @@ lecturerInvitationConfig = InvitationConfig{..} invitationHeading (Entity _ Course{..}) _ = return . SomeMessage $ MsgCourseLecInviteHeading $ CI.original courseName invitationExplanation _ _ = return [ihamlet|_{SomeMessage MsgCourseLecInviteExplanation}|] invitationTokenConfig _ _ = do - itAuthority <- Right <$> liftHandler requireAuthId + itAuthority <- HashSet.singleton . Right <$> liftHandler requireAuthId return $ InvitationTokenConfig itAuthority Nothing Nothing Nothing invitationRestriction _ _ = return Authorized invitationForm _ (InvDBDataLecturer mlType, _) _ = hoistAForm liftHandler $ toJunction <$> case mlType of diff --git a/src/Handler/Course/List.hs b/src/Handler/Course/List.hs index cc499243b..794fa74a7 100644 --- a/src/Handler/Course/List.hs +++ b/src/Handler/Course/List.hs @@ -61,11 +61,13 @@ type CourseTableExpr = E.SqlExpr (Entity Course) `E.InnerJoin` E.SqlExpr (Entity course2Participants :: CourseTableExpr -> E.SqlExpr (E.Value Int) course2Participants (course `E.InnerJoin` _school) = E.subSelectCount . E.from $ \courseParticipant -> E.where_ $ courseParticipant E.^. CourseParticipantCourse E.==. course E.^. CourseId + E.&&. courseParticipant E.^. CourseParticipantState E.==. E.val CourseParticipantActive course2Registered :: Maybe UserId -> CourseTableExpr -> E.SqlExpr (E.Value Bool) course2Registered muid (course `E.InnerJoin` _school) = E.exists . E.from $ \courseParticipant -> E.where_ $ courseParticipant E.^. CourseParticipantCourse E.==. course E.^. CourseId E.&&. E.just (courseParticipant E.^. CourseParticipantUser) E.==. E.val muid + E.&&. courseParticipant E.^. CourseParticipantState E.==. E.val CourseParticipantActive makeCourseTable :: ( IsDBTable m x, ToSortable h, Functor h, DBResult m x ~ ((), Widget) ) => _ -> Colonnade h CourseTableData (DBCell m x) -> PSValidator m x -> DB Widget @@ -83,10 +85,10 @@ makeCourseTable whereClause colChoices psValidator = do E.on $ user E.^. UserId E.==. lecturer E.^. LecturerUser E.where_ $ cid E.==. lecturer E.^. LecturerCourse E.&&. lecturer E.^. LecturerType E.==. E.val CourseLecturer return user - dbtProj :: DBRow _ -> MaybeT DB CourseTableData + dbtProj :: DBRow _ -> DB CourseTableData dbtProj = traverse $ \(course, E.Value participants, E.Value registered, school) -> do - lecturerList <- lift $ E.select $ E.from $ lecturerQuery $ E.val $ entityKey course - courseAlloc <- lift $ getBy (UniqueAllocationCourse $ entityKey course) + lecturerList <- E.select $ E.from $ lecturerQuery $ E.val $ entityKey course + courseAlloc <- getBy (UniqueAllocationCourse $ entityKey course) >>= traverse (getJustEntity . allocationCourseAllocation . entityVal) return (course, participants, registered, school, lecturerList, courseAlloc) snd <$> dbTable psValidator DBTable diff --git a/src/Handler/Course/ParticipantInvite.hs b/src/Handler/Course/ParticipantInvite.hs index cfdba213a..529bed63d 100644 --- a/src/Handler/Course/ParticipantInvite.hs +++ b/src/Handler/Course/ParticipantInvite.hs @@ -7,6 +7,7 @@ module Handler.Course.ParticipantInvite , AddParticipantsResult(..) , addParticipantsResultMessages , registerUsers, registerUser + , registerUsers', registerUser' ) where import Import @@ -14,10 +15,12 @@ import Import import Utils.Form import Handler.Utils import Handler.Utils.Invitations +import Handler.Utils.Course import qualified Data.CaseInsensitive as CI import qualified Data.Set as Set +import qualified Data.Map as Map import Jobs.Queue @@ -29,6 +32,8 @@ import Control.Monad.Except (MonadError(..)) import Generics.Deriving.Monoid (memptydefault, mappenddefault) +import qualified Data.HashSet as HashSet + -- Invitations for ordinary participants of this course instance IsInvitableJunction CourseParticipant where @@ -37,16 +42,19 @@ instance IsInvitableJunction CourseParticipant where { jParticipantRegistration :: UTCTime , jParticipantField :: Maybe StudyFeaturesId , jParticipantAllocated :: Maybe AllocationId + , jParticipantState :: CourseParticipantState } deriving (Eq, Ord, Read, Show, Generic, Typeable) data InvitationDBData CourseParticipant = InvDBDataParticipant -- no data needed in DB to manage participant invitation deriving (Eq, Ord, Read, Show, Generic, Typeable) data InvitationTokenData CourseParticipant = InvTokenDataParticipant + { invTokenParticipantSubmissionGroup :: Maybe SubmissionGroupName + } deriving (Eq, Ord, Read, Show, Generic, Typeable) _InvitableJunction = iso - (\CourseParticipant{..} -> (courseParticipantUser, courseParticipantCourse, JunctionParticipant courseParticipantRegistration courseParticipantField courseParticipantAllocated)) - (\(courseParticipantUser, courseParticipantCourse, JunctionParticipant courseParticipantRegistration courseParticipantField courseParticipantAllocated) -> CourseParticipant{..}) + (\CourseParticipant{..} -> (courseParticipantUser, courseParticipantCourse, JunctionParticipant courseParticipantRegistration courseParticipantField courseParticipantAllocated courseParticipantState)) + (\(courseParticipantUser, courseParticipantCourse, JunctionParticipant courseParticipantRegistration courseParticipantField courseParticipantAllocated courseParticipantState) -> CourseParticipant{..}) instance ToJSON (InvitableJunction CourseParticipant) where toJSON = genericToJSON defaultOptions { fieldLabelModifier = camelToPathPiece' 1 } @@ -61,10 +69,10 @@ instance FromJSON (InvitationDBData CourseParticipant) where parseJSON = genericParseJSON defaultOptions { fieldLabelModifier = camelToPathPiece' 3 } instance ToJSON (InvitationTokenData CourseParticipant) where - toJSON = genericToJSON defaultOptions { constructorTagModifier = camelToPathPiece' 3 } - toEncoding = genericToEncoding defaultOptions { constructorTagModifier = camelToPathPiece' 3 } + toJSON = genericToJSON defaultOptions { fieldLabelModifier = camelToPathPiece' 3, omitNothingFields = True } + toEncoding = genericToEncoding defaultOptions { fieldLabelModifier = camelToPathPiece' 3, omitNothingFields = True } instance FromJSON (InvitationTokenData CourseParticipant) where - parseJSON = genericParseJSON defaultOptions { constructorTagModifier = camelToPathPiece' 3 } + parseJSON = genericParseJSON defaultOptions { fieldLabelModifier = camelToPathPiece' 3, omitNothingFields = True } participantInvitationConfig :: InvitationConfig CourseParticipant participantInvitationConfig = InvitationConfig{..} @@ -81,17 +89,19 @@ participantInvitationConfig = InvitationConfig{..} invitationHeading (Entity _ Course{..}) _ = return . SomeMessage $ MsgCourseParticipantInviteHeading $ CI.original courseName invitationExplanation _ _ = return [ihamlet|_{SomeMessage MsgCourseParticipantInviteExplanation}|] invitationTokenConfig _ _ = do - itAuthority <- Right <$> liftHandler requireAuthId + itAuthority <- HashSet.singleton . Right <$> liftHandler requireAuthId return $ InvitationTokenConfig itAuthority Nothing Nothing Nothing invitationRestriction _ _ = return Authorized invitationForm (Entity _ Course{..}) _ uid = hoistAForm lift . wFormToAForm $ do now <- liftIO getCurrentTime studyFeatures <- wreq (studyFeaturesFieldFor Nothing False [] $ Just uid) (fslI MsgCourseStudyFeature & setTooltip MsgCourseStudyFeatureTip) Nothing - return . fmap (, ()) $ JunctionParticipant <$> pure now <*> studyFeatures <*> pure Nothing - invitationInsertHook _ _ _ CourseParticipant{..} _ act = do - res <- act + return . fmap (, ()) $ JunctionParticipant <$> pure now <*> studyFeatures <*> pure Nothing <*> pure CourseParticipantActive + invitationInsertHook _ _ (_, InvTokenDataParticipant{..}) CourseParticipant{..} _ act = do + deleteBy $ UniqueParticipant courseParticipantUser courseParticipantCourse -- there are no foreign key references to @{CourseParticipant}; therefor we can delete and recreate to simulate upsert + res <- act -- insertUnique audit $ TransactionCourseParticipantEdit courseParticipantCourse courseParticipantUser + void $ setUserSubmissionGroup courseParticipantCourse courseParticipantUser invTokenParticipantSubmissionGroup return res invitationSuccessMsg (Entity _ Course{..}) _ = return . SomeMessage $ MsgCourseParticipantInvitationAccepted (CI.original courseName) @@ -116,11 +126,17 @@ postCAddUserR tid ssh csh = do cid <- runDB . getKeyBy404 $ TermSchoolCourseShort tid ssh csh ((usersToEnlist,formWgt),formEncoding) <- runFormPost . renderWForm FormStandard $ do enlist <- wreq checkBoxField (fslI MsgCourseParticipantEnlistDirectly) (Just False) - wreq (multiUserField (maybe True not $ formResultToMaybe enlist) Nothing) - (fslI MsgCourseParticipantInviteField & setTooltip MsgMultiEmailFieldTip) Nothing + + let submissionGroupOpts = optionsPersist [SubmissionGroupCourse ==. cid] [Asc SubmissionGroupName] submissionGroupName <&> fmap (submissionGroupName . entityVal) + mbGrp <- wopt (textField & cfStrip & cfCI & addDatalist submissionGroupOpts) (fslI MsgSubmissionGroup & setTooltip MsgSubmissionGroupEmptyIsUnsetTip) Nothing + + users <- wreq (multiUserField (maybe True not $ formResultToMaybe enlist) Nothing) + (fslI MsgCourseParticipantInviteField & setTooltip MsgMultiEmailFieldTip) Nothing + + return $ Map.fromSet . const <$> mbGrp <*> users formResultModal usersToEnlist (CourseR tid ssh csh CUsersR) $ - hoist runDBJobs . registerUsers cid + hoist runDBJobs . registerUsers' cid let heading = prependCourseTitle tid ssh csh MsgCourseParticipantsRegisterHeading @@ -133,13 +149,16 @@ postCAddUserR tid ssh csh = do } registerUsers :: CourseId -> Set (Either UserEmail UserId) -> WriterT [Message] (YesodJobDB UniWorX) () -registerUsers cid users = do - let (emails,uids) = partitionEithers $ Set.toList users +registerUsers cid = registerUsers' cid . Map.fromSet (const Nothing) + +registerUsers' :: CourseId -> Map (Either UserEmail UserId) (Maybe SubmissionGroupName) -> WriterT [Message] (YesodJobDB UniWorX) () +registerUsers' cid users = do + let (emails,uids) = partitionKeysEither users -- send Invitation eMails to unkown users - lift $ sinkInvitationsF participantInvitationConfig [(mail,cid,(InvDBDataParticipant,InvTokenDataParticipant)) | mail <- emails] + lift $ sinkInvitationsF participantInvitationConfig [(mail,cid,(InvDBDataParticipant,InvTokenDataParticipant{..})) | (mail, invTokenParticipantSubmissionGroup) <- Map.toList emails] -- register known users - tell <=< lift . addParticipantsResultMessages <=< lift . execWriterT $ mapM_ (registerUser cid) uids + tell <=< lift . addParticipantsResultMessages <=< lift . execWriterT $ imapM_ (registerUser' cid) uids unless (null emails) $ tell . pure <=< messageI Success . MsgCourseParticipantsInvited $ length emails @@ -170,8 +189,14 @@ addParticipantsResultMessages AddParticipantsResult{..} = execWriterT $ do registerUser :: CourseId -> UserId -> WriterT AddParticipantsResult (YesodJobDB UniWorX) () -registerUser cid uid = exceptT tell tell $ do - whenM (lift . lift . existsBy $ UniqueParticipant uid cid) $ +registerUser cid uid = registerUser' cid uid Nothing + +registerUser' :: CourseId + -> UserId + -> Maybe SubmissionGroupName + -> WriterT AddParticipantsResult (YesodJobDB UniWorX) () +registerUser' cid uid mbGrp = exceptT tell tell $ do + whenM (lift . lift $ exists [CourseParticipantCourse ==. cid, CourseParticipantUser ==. uid, CourseParticipantState ==. CourseParticipantActive]) $ throwError $ mempty { aurAlreadyRegistered = Set.singleton uid } features <- lift . lift $ selectKeysList [ StudyFeaturesUser ==. uid, StudyFeaturesValid ==. True, StudyFeaturesType ==. FieldPrimary ] [] @@ -187,15 +212,24 @@ registerUser cid uid = exceptT tell tell $ do = Nothing courseParticipantRegistration <- liftIO getCurrentTime - void . lift . lift . insert $ CourseParticipant - { courseParticipantCourse = cid - , courseParticipantUser = uid - , courseParticipantAllocated = Nothing - , .. - } + void . lift . lift $ upsert + CourseParticipant + { courseParticipantCourse = cid + , courseParticipantUser = uid + , courseParticipantAllocated = Nothing + , courseParticipantState = CourseParticipantActive + , .. + } + [ CourseParticipantRegistration =. courseParticipantRegistration + , CourseParticipantField =. courseParticipantField + , CourseParticipantAllocated =. Nothing + , CourseParticipantState =. CourseParticipantActive + ] lift . lift . audit $ TransactionCourseParticipantEdit cid uid lift . lift . queueDBJob . JobQueueNotification $ NotificationCourseRegistered uid cid + void . lift . lift $ setUserSubmissionGroup cid uid mbGrp + return $ case courseParticipantField of Nothing -> mempty { aurNoUniquePrimaryField = Set.singleton uid } Just _ -> mempty { aurSuccess = Set.singleton uid } diff --git a/src/Handler/Course/Register.hs b/src/Handler/Course/Register.hs index fe3eea1a9..506453d8e 100644 --- a/src/Handler/Course/Register.hs +++ b/src/Handler/Course/Register.hs @@ -48,7 +48,7 @@ courseRegisterForm :: (MonadHandler m, HandlerSite m ~ UniWorX) => Entity Course courseRegisterForm (Entity cid Course{..}) = liftHandler $ do muid <- maybeAuthId (registration, application) <- runDB $ do - registration <- fmap join . for muid $ getBy . flip UniqueParticipant cid + registration <- fmap join . for muid $ fmap (assertM . has $ _entityVal . _courseParticipantState . _CourseParticipantActive) . getBy . flip UniqueParticipant cid application <- fmap (listToMaybe =<<) . for muid $ \uid -> selectList [CourseApplicationCourse ==. cid, CourseApplicationUser ==. uid, CourseApplicationAllocation ==. Nothing] [] return (registration, application) let btn | courseApplicationsRequired @@ -142,6 +142,8 @@ courseRegisterForm (Entity cid Course{..}) = liftHandler $ do -> aFormToWForm $ fileUploadForm False (fslI . mkFs) courseApplicationsFiles when (is _Just $ registration >>= courseParticipantAllocated . entityVal) $ + wformMessage =<< messageIconI Warning IconExamRegisterFalse MsgCourseDeregistrationNoShow + when (is _Just (registration >>= courseParticipantAllocated . entityVal) && courseDeregisterNoShow) $ wformMessage =<< messageIconI Warning IconEnrolFalse MsgCourseDeregistrationAllocationLog return $ CourseRegisterForm @@ -160,7 +162,7 @@ getCRegisterR tid ssh csh = do Nothing -> addMessageI Info MsgLoginNecessary (Just uid) -> runDB $ do cid <- getKeyBy404 $ TermSchoolCourseShort tid ssh csh - registration <- getBy (UniqueParticipant uid cid) + registration <- fmap (assertM . has $ _entityVal . _courseParticipantState . _CourseParticipantActive) . getBy $ UniqueParticipant uid cid when (isNothing registration) $ addMessageI Warning MsgRegisterRetry redirect $ CourseR tid ssh csh CShowR @@ -199,22 +201,40 @@ postCRegisterR tid ssh csh = do = return $ Just () mkRegistration = do audit $ TransactionCourseParticipantEdit cid uid - insertUnique $ CourseParticipant cid uid cTime crfStudyFeatures Nothing + entityKey <$> upsert + (CourseParticipant cid uid cTime crfStudyFeatures Nothing CourseParticipantActive) + [ CourseParticipantRegistration =. cTime + , CourseParticipantField =. crfStudyFeatures + , CourseParticipantAllocated =. Nothing + , CourseParticipantState =. CourseParticipantActive + ] case courseRegisterButton of BtnCourseRegister -> runDB $ do - regOk <- (\app reg -> (,) <$> app <*> reg) <$> mkApplication <*> mkRegistration + regOk <- (\app reg -> (, reg) <$> app) <$> mkApplication <*> mkRegistration case regOk of Nothing -> transactionUndo Just _ -> addMessageIconI Success IconEnrolTrue MsgCourseRegisterOk BtnCourseDeregister -> runDB $ do - part <- getBy $ UniqueParticipant uid cid + part <- fmap (assertM . has $ _entityVal . _courseParticipantState . _CourseParticipantActive) . getBy $ UniqueParticipant uid cid forM_ part $ \(Entity _partId CourseParticipant{..}) -> do + deregisterParticipant uid cid + when (is _Just courseParticipantAllocated) $ do + updateBy (UniqueParticipant uid cid) [ CourseParticipantState =. CourseParticipantInactive courseDeregisterNoShow ] + now <- liftIO getCurrentTime insert_ $ AllocationDeregister courseParticipantUser (Just courseParticipantCourse) now Nothing - - deregisterParticipant uid cid + let recordNoShow eId = do + didRecord <- is _Just <$> insertUnique ExamResult + { examResultExam = eId + , examResultUser = uid + , examResultResult = ExamNoShow + , examResultLastChanged = now + } + when didRecord $ + audit $ TransactionExamResultEdit eId uid + when courseDeregisterNoShow . runConduit $ selectKeys [ ExamCourse ==. cid ] [] .| C.mapM_ recordNoShow addMessageIconI Info IconEnrolFalse MsgCourseDeregisterOk BtnCourseApply -> runDB $ do @@ -243,9 +263,9 @@ deleteApplicationFiles appId = do deregisterParticipant :: UserId -> CourseId -> DB () deregisterParticipant uid cid = do deleteApplications uid cid - part <- getBy $ UniqueParticipant uid cid + part <- fmap (assertM . has $ _entityVal . _courseParticipantState . _CourseParticipantActive) . getBy $ UniqueParticipant uid cid forM_ part $ \(Entity partId CourseParticipant{..}) -> do - delete $ partId + update partId [CourseParticipantState =. CourseParticipantInactive False] audit $ TransactionCourseParticipantDeleted cid uid examRegistrations <- E.select . E.from $ \(examRegistration `E.InnerJoin` exam) -> do diff --git a/src/Handler/Course/Show.hs b/src/Handler/Course/Show.hs index e07eec99f..e74d226da 100644 --- a/src/Handler/Course/Show.hs +++ b/src/Handler/Course/Show.hs @@ -20,16 +20,19 @@ import Handler.Course.Register import qualified Data.Conduit.List as C +import Handler.Exam.List (mkExamTable) + getCShowR :: TermId -> SchoolId -> CourseShorthand -> Handler Html getCShowR tid ssh csh = do mbAid <- maybeAuthId - (cid,course,schoolName,participants,registration,lecturers,assistants,correctors,tutors,mAllocation,hasApplicationTemplate,mApplication,news,events) <- runDB . maybeT notFound $ do + (cid,course,schoolName,participants,registration,lecturers,assistants,correctors,tutors,mAllocation,hasApplicationTemplate,mApplication,news,events,submissionGroup) <- runDB . maybeT notFound $ do [(E.Entity cid course, E.Value schoolName, E.Value participants, fmap entityVal -> registration)] <- lift . E.select . E.from $ \((school `E.InnerJoin` course) `E.LeftOuterJoin` participant) -> do E.on $ E.just (course E.^. CourseId) E.==. participant E.?. CourseParticipantCourse E.&&. E.val mbAid E.==. participant E.?. CourseParticipantUser + E.&&. participant E.?. CourseParticipantState E.==. E.just (E.val CourseParticipantActive) E.on $ course E.^. CourseSchool E.==. school E.^. SchoolId E.where_ $ course E.^. CourseTerm E.==. E.val tid E.&&. course E.^. CourseSchool E.==. E.val ssh @@ -38,6 +41,7 @@ getCShowR tid ssh csh = do let numParticipants :: E.SqlExpr (E.Value Int) numParticipants = E.subSelectCount . E.from $ \part -> E.where_ $ part E.^. CourseParticipantCourse E.==. course E.^. CourseId + E.&&. part E.^. CourseParticipantState E.==. E.val CourseParticipantActive return (course,school E.^. SchoolName, numParticipants, participant) staff <- lift . E.select $ E.from $ \(lecturer `E.InnerJoin` user) -> do E.on $ lecturer E.^. LecturerUser E.==. user E.^. UserId @@ -89,8 +93,19 @@ getCShowR tid ssh csh = do events' <- fmap (sortOn $ courseEventTime . entityVal) . lift $ selectList [ CourseEventCourse ==. cid ] [] events <- mapM (\(Entity evId ev) -> (, ev) <$> encrypt evId) events' + + hasSubmissionGroups <- lift . E.selectExists . E.from $ \(submissionGroupUser `E.InnerJoin` submissionGroup) -> do + E.on $ submissionGroupUser E.^. SubmissionGroupUserSubmissionGroup E.==. submissionGroup E.^. SubmissionGroupId + E.where_ $ submissionGroup E.^. SubmissionGroupCourse E.==. E.val cid + submissionGroup' <- lift . for mbAid $ \uid -> + fmap (listToMaybe . fmap E.unValue) . E.select . E.from $ \(submissionGroupUser `E.InnerJoin` submissionGroup) -> do + E.on $ submissionGroupUser E.^. SubmissionGroupUserSubmissionGroup E.==. submissionGroup E.^. SubmissionGroupId + E.where_ $ submissionGroup E.^. SubmissionGroupCourse E.==. E.val cid + E.where_ $ submissionGroupUser E.^. SubmissionGroupUserUser E.==. E.val uid + return $ submissionGroup E.^. SubmissionGroupName + let submissionGroup = guardOnM (hasSubmissionGroups && is _Just registration) submissionGroup' - return (cid,course,schoolName,participants,registration,lecturers,assistants,correctors,tutors,mAllocation,hasApplicationTemplate,mApplication,news,events) + return (cid,course,schoolName,participants,registration,lecturers,assistants,correctors,tutors,mAllocation,hasApplicationTemplate,mApplication,news,events,submissionGroup) let mDereg' = maybe id min (allocationOverrideDeregister =<< mAllocation) <$> courseDeregisterUntil course mDereg <- traverse (formatTime SelFormatDateTime) mDereg' @@ -125,7 +140,7 @@ getCShowR tid ssh csh = do dbtColonnade = dbColonnade $ mconcat [ sortable (Just "type") (i18nCell MsgTutorialType) $ \DBRow{ dbrOutput = Entity _ Tutorial{..} } -> textCell $ CI.original tutorialType , sortable (Just "name") (i18nCell MsgTutorialName) $ \DBRow{ dbrOutput = Entity _ Tutorial{..} } -> indicatorCell <> anchorCell (CTutorialR tid ssh csh tutorialName TUsersR) [whamlet|#{tutorialName}|] - , sortable Nothing (i18nCell MsgTutorialTutors) $ \DBRow{ dbrOutput = Entity tutid _ } -> sqlCell $ do + , sortable (Just "tutors") (i18nCell MsgTutorialTutors) $ \DBRow{ dbrOutput = Entity tutid _ } -> sqlCell $ do tutTutors <- fmap (map $(unValueN 3)) . E.select . E.from $ \(tutor `E.InnerJoin` user) -> do E.on $ tutor E.^. TutorUser E.==. user E.^. UserId E.where_ $ tutor E.^. TutorTutorial E.==. E.val tutid @@ -174,6 +189,12 @@ getCShowR tid ssh csh = do , ("register-from", SortColumn $ \tutorial -> tutorial E.^. TutorialRegisterFrom ) , ("register-to", SortColumn $ \tutorial -> tutorial E.^. TutorialRegisterTo ) , ("deregister-until", SortColumn $ \tutorial -> tutorial E.^. TutorialDeregisterUntil ) + , ( "tutors" + , SortColumn $ \tutorial -> E.subSelectMaybe . E.from $ \(tutor `E.InnerJoin` user) -> do + E.on $ tutor E.^. TutorUser E.==. user E.^. UserId + E.where_ $ tutorial E.^. TutorialId E.==. tutor E.^. TutorTutorial + return . E.min_ $ user E.^. UserSurname + ) ] dbtFilter = Map.empty dbtFilterUI = const mempty @@ -188,73 +209,7 @@ getCShowR tid ssh csh = do & defaultSorting [SortAscBy "type", SortAscBy "name"] (Any hasTutorials, tutorialTable) <- runDB $ dbTable tutorialDBTableValidator tutorialDBTable - let - examDBTable = DBTable{..} - where - dbtSQLQuery exam = do - E.where_ $ exam E.^. ExamCourse E.==. E.val cid - return exam - dbtRowKey = (E.^. ExamId) - dbtProj r@DBRow{ dbrOutput = Entity _ Exam{..} } = do - guardM . hasReadAccessTo $ CExamR tid ssh csh examName EShowR - return r - dbtColonnade = dbColonnade $ mconcat - [ sortable (Just "name") (i18nCell MsgExamName) $ \DBRow{ dbrOutput = Entity _ Exam{..} } -> indicatorCell <> anchorCell (CExamR tid ssh csh examName EShowR) examName - , sortable (Just "register-from") (i18nCell MsgExamRegisterFrom) $ \DBRow { dbrOutput = Entity _ Exam{..} } -> maybe mempty dateTimeCell examRegisterFrom - , sortable (Just "register-to") (i18nCell MsgExamRegisterTo) $ \DBRow { dbrOutput = Entity _ Exam{..} } -> maybe mempty dateTimeCell examRegisterTo - , sortable (Just "time") (i18nCell MsgExamTime) $ \DBRow{ dbrOutput = Entity _ Exam{..} } -> maybe mempty (cell . flip (formatTimeRangeW SelFormatDateTime) examEnd) examStart - , sortable (Just "registered") (i18nCell MsgExamRegistration ) $ \DBRow{ dbrOutput = Entity eId Exam{..} } -> sqlCell $ do - mayRegister <- (== Authorized) <$> evalAccessDB (CExamR tid ssh csh examName ERegisterR) True - isRegistered <- case mbAid of - Nothing -> return False - Just uid -> existsBy $ UniqueExamRegistration eId uid - let label = bool MsgExamNotRegistered MsgExamRegistered isRegistered - examUrl = CExamR tid ssh csh examName EShowR - if | mayRegister -> return $ simpleLinkI (SomeMessage label) examUrl - | otherwise -> return [whamlet|_{label}|] - -- , sortable Nothing mempty $ \DBRow{ dbrOutput = Entity eId Exam{..} } -> sqlCell $ do - -- mayRegister <- (== Authorized) <$> evalAccessDB (CExamR tid ssh csh examName ERegisterR) True - -- isRegistered <- case mbAid of - -- Nothing -> return False - -- Just uid -> existsBy $ UniqueExamRegistration eId uid - -- if - -- | mayRegister -> do - -- (examRegisterForm, examRegisterEnctype) <- liftHandler . generateFormPost . buttonForm' $ bool [BtnExamRegister] [BtnExamDeregister] isRegistered - -- return $ wrapForm examRegisterForm def - -- { formAction = Just . SomeRoute $ CExamR tid ssh csh examName ERegisterR - -- , formEncoding = examRegisterEnctype - -- , formSubmit = FormNoSubmit - -- } - -- | isRegistered -> return [whamlet|_{MsgExamRegistered}|] - -- | otherwise -> return mempty - ] - dbtSorting = Map.fromList - [ ("name", SortColumn $ \exam -> exam E.^. ExamName ) - , ("time", SortColumn $ \exam -> exam E.^. ExamStart ) - , ("register-from", SortColumn $ \exam -> exam E.^. ExamRegisterFrom ) - , ("register-to", SortColumn $ \exam -> exam E.^. ExamRegisterTo ) - , ("visible", SortColumn $ \exam -> exam E.^. ExamVisibleFrom ) - , ("registered", SortColumn $ \exam -> - case mbAid of - Nothing -> E.false - Just uid -> - E.exists $ E.from $ \reg -> do - E.where_ $ reg E.^. ExamRegistrationUser E.==. E.val uid - E.where_ $ reg E.^. ExamRegistrationExam E.==. exam E.^. ExamId - ) - ] - dbtFilter = Map.empty - dbtFilterUI = const mempty - dbtStyle = def - dbtParams = def - dbtIdent :: Text - dbtIdent = "exams" - dbtCsvEncode = noCsvEncode - dbtCsvDecode = Nothing - - examDBTableValidator = def - & defaultSorting [SortAscBy "time"] - (Any hasExams, examTable) <- runDB $ dbTable examDBTableValidator examDBTable + (Any hasExams, examTable) <- runDB . mkExamTable $ Entity cid course let visibleNews = any (view _3) news showNewsFiles fs = and @@ -262,6 +217,7 @@ getCShowR tid ssh csh = do , length fs <= 3 , all (notElem pathSeparator . view _2) fs ] + hiddenEventNotes = all (\(_,CourseEvent{..}) -> is _Nothing courseEventNote) events mayCreateNews <- hasWriteAccessTo $ CourseR tid ssh csh CNewsNewR mayCreateEvents <- hasWriteAccessTo $ CourseR tid ssh csh CEventsNewR diff --git a/src/Handler/Course/User.hs b/src/Handler/Course/User.hs index aef35f333..ba223ceaa 100644 --- a/src/Handler/Course/User.hs +++ b/src/Handler/Course/User.hs @@ -6,9 +6,13 @@ import Import import Utils.Form import Handler.Utils +import Handler.Utils.SheetType import Database.Esqueleto.Utils.TH import qualified Database.Esqueleto as E +import qualified Database.Esqueleto.Utils as E + +import Database.Persist.Sql (deleteWhereCount) import Text.Blaze.Html.Renderer.Text (renderHtml) @@ -16,29 +20,196 @@ import Handler.Course.Register import Jobs.Queue +import Handler.Corrections + +import qualified Data.Map as Map +import qualified Data.Text as Text +import qualified Data.CaseInsensitive as CI + +import qualified Data.Conduit.Combinators as C + + +data ExamAction = ExamDeregister + | ExamSetResult + deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic, Typeable) + deriving anyclass (Universe, Finite) +nullaryPathPiece ''ExamAction $ camelToPathPiece' 1 +embedRenderMessage ''UniWorX ''ExamAction $ Text.replace "Exam" "ExamUser" + +data ExamActionData = ExamDeregisterData + | ExamSetResultData (Maybe ExamResultPassedGrade) + +data TutorialAction = TutorialDeregister + deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic, Typeable) + deriving anyclass (Universe, Finite) +nullaryPathPiece ''TutorialAction $ camelToPathPiece' 1 +embedRenderMessage ''UniWorX ''TutorialAction $ Text.replace "Tutorial" "TutorialUser" + +data TutorialActionData = TutorialDeregisterData + getCUserR, postCUserR :: TermId -> SchoolId -> CourseShorthand -> CryptoUUIDUser -> Handler Html getCUserR = postCUserR postCUserR tid ssh csh uCId = do - -- Has authorization checks (OR): - -- - -- - User is current member of course - -- - User has submitted in course - -- - User is member of registered group for course - -- - User is member of a tutorial for course - -- - User is corrector for course - -- - User is a tutor for course - -- - User is a lecturer for course - let currentRoute = CourseR tid ssh csh (CUserR uCId) - Entity dozentId (userShowSex -> showSex) <- requireAuth - uid <- decrypt uCId - -- DB reads - (cid, User{..}, mRegistration, thisUniqueNote, noteText, noteEdits, studies) <- runDB $ do - cid <- getKeyBy404 $ TermSchoolCourseShort tid ssh csh - -- Abfrage Benutzerdaten + showSex <- maybe False (userShowSex . entityVal) <$> maybeAuth + + (course, user@(Entity _ User{..}), registered) <- runDB $ do + uid <- decrypt uCId + course@(Entity cid _) <- getBy404 $ TermSchoolCourseShort tid ssh csh user <- get404 uid - registration <- getBy (UniqueParticipant uid cid) - -- Abfrage Teilnehmernotiz + registered <- exists [ CourseParticipantCourse ==. cid, CourseParticipantUser ==. uid, CourseParticipantState ==. CourseParticipantActive ] + + return (course, Entity uid user, registered) + + sections <- mapM (runMaybeT . ($ user) . ($ course)) + [ courseUserProfileSection + , courseUserNoteSection + , courseUserExamsSection + , courseUserTutorialsSection + , courseUserSubmissionsSection + ] + + -- generate output + let headingLong + | registered + , Just sex <- guardOn showSex =<< userSex + = [whamlet|^{nameWidget userDisplayName userSurname} (_{ShortSex sex}), _{MsgCourseMemberOf} #{csh} #{tid}|] + | registered + = [whamlet|^{nameWidget userDisplayName userSurname}, _{MsgCourseMemberOf} #{csh} #{tid}|] + | Just sex <- guardOn showSex =<< userSex + = [whamlet|^{nameWidget userDisplayName userSurname} (_{ShortSex sex}), _{MsgCourseAssociatedWith} #{csh} #{tid}|] + | otherwise + = [whamlet|^{nameWidget userDisplayName userSurname}, _{MsgCourseAssociatedWith} #{csh} #{tid}|] + headingShort = prependCourseTitle tid ssh csh $ SomeMessage userDisplayName + siteLayout headingLong $ do + setTitleI headingShort + + forM_ sections . fromMaybe $ return () + +courseUserProfileSection :: Entity Course -> Entity User -> MaybeT Handler Widget +courseUserProfileSection (Entity cid Course{..}) (Entity uid User{ userShowSex = _, ..}) = do + showSex <- maybe False (userShowSex . entityVal) <$> maybeAuth + currentRoute <- MaybeT getCurrentRoute + + (mRegistration, studies) <- lift . runDB $ do + registration <- fmap (assertM . has $ _entityVal . _courseParticipantState . _CourseParticipantActive) . getBy $ UniqueParticipant uid cid + studies <- E.select $ E.from $ \(studydegree `E.InnerJoin` studyfeat `E.InnerJoin` studyterms) -> do + E.where_ $ studyfeat E.^. StudyFeaturesUser E.==. E.val uid + E.on $ studyfeat E.^. StudyFeaturesField E.==. studyterms E.^. StudyTermsId + E.on $ studyfeat E.^. StudyFeaturesDegree E.==. studydegree E.^. StudyDegreeId + return (studyfeat, studydegree, studyterms) + return (registration, studies) + + ((regFieldRes, regFieldView), regFieldEnctype) <- lift . runFormPost . identifyForm FIDcRegField $ \csrf -> + let currentField :: Maybe (Maybe StudyFeaturesId) + currentField = courseParticipantField . entityVal <$> mRegistration + in over _2 ((toWidget csrf <>) . fvInput) <$> mreq (studyFeaturesFieldFor Nothing True (maybeToList $ join currentField) $ Just uid) ("" & addAutosubmit) currentField + + let registrationFieldFrag :: Text + registrationFieldFrag = "registration-field" + regFieldWidget = wrapForm regFieldView FormSettings + { formMethod = POST + , formAction = Just . SomeRoute $ currentRoute :#: registrationFieldFrag + , formEncoding = regFieldEnctype + , formAttrs = [] + , formSubmit = FormAutoSubmit + , formAnchor = Just registrationFieldFrag + } + for_ mRegistration $ \(Entity pId CourseParticipant{..}) -> + formResult regFieldRes $ \courseParticipantField' -> do + lift . runDB $ do + update pId [ CourseParticipantField =. courseParticipantField' ] + audit $ TransactionCourseParticipantEdit cid uid + addMessageI Success MsgCourseStudyFeatureUpdated + redirect $ currentRoute :#: registrationFieldFrag + + mayRegister <- hasWriteAccessTo $ CourseR courseTerm courseSchool courseShorthand CAddUserR + let regButton + | is _Just mRegistration = BtnCourseDeregister + | otherwise = BtnCourseRegister + ((regButtonRes, regButtonView), regButtonEnctype) <- lift . runFormPost . identifyForm FIDcRegButton $ + if | is _Just $ courseParticipantAllocated . entityVal =<< mRegistration + -> renderWForm FormStandard $ fmap (regButton, ) + <$ (wformMessage =<< messageIconI Warning IconEnrolFalse MsgCourseDeregistrationAllocationShouldLogTip) + <*> optionalActionW ((,) + <$> areq (textField & cfStrip & guardField (not . null)) (fslI MsgCourseDeregistrationAllocationReason & setTooltip MsgCourseDeregistrationAllocationReasonTip) Nothing + <*> apopt checkBoxField (fslI MsgCourseDeregistrationAllocationNoShow & setTooltip MsgCourseDeregistrationAllocationNoShowTip) Nothing + ) (fslI MsgCourseDeregistrationAllocationShouldLog) (Just True) + | otherwise + -> \csrf -> pure (FormSuccess (regButton, Nothing), toWidget csrf) + + let registrationButtonFrag :: Text + registrationButtonFrag = "registration-button" + regButtonWidget = wrapForm' regButton regButtonView FormSettings + { formMethod = POST + , formAction = Just . SomeRoute $ currentRoute :#: registrationButtonFrag + , formEncoding = regButtonEnctype + , formAttrs = [] + , formSubmit = FormSubmit + , formAnchor = Just registrationButtonFrag + } + formResult regButtonRes $ \case + _ + | not mayRegister + -> permissionDenied "User may not be registered" + (BtnCourseDeregister, mbReason) + | Just (Entity _pId CourseParticipant{..}) <- mRegistration + -> do + lift . runDB $ do + deregisterParticipant courseParticipantUser courseParticipantCourse + + whenIsJust mbReason $ \(reason, noShow) -> do + updateBy (UniqueParticipant uid cid) [ CourseParticipantState =. CourseParticipantInactive noShow ] + + now <- liftIO getCurrentTime + insert_ $ AllocationDeregister courseParticipantUser (Just cid) now (Just reason) + let recordNoShow eId = do + didRecord <- is _Just <$> insertUnique ExamResult + { examResultExam = eId + , examResultUser = uid + , examResultResult = ExamNoShow + , examResultLastChanged = now + } + when didRecord $ + audit $ TransactionExamResultEdit eId uid + when noShow . runConduit $ selectKeys [ ExamCourse ==. cid ] [] .| C.mapM_ recordNoShow + addMessageIconI Info IconEnrolFalse MsgCourseDeregisterOk + redirect $ CourseR courseTerm courseSchool courseShorthand CUsersR + | otherwise + -> invalidArgs ["User not registered"] + (BtnCourseRegister, _) -> do + now <- liftIO getCurrentTime + let field + | [(Entity featId _, _, _)] <- filter (\(Entity _ StudyFeatures{..}, _, _) -> studyFeaturesValid) studies + = Just featId + | otherwise + = Nothing + lift . runDBJobs $ do + void $ upsert + (CourseParticipant cid uid now field Nothing CourseParticipantActive) + [ CourseParticipantRegistration =. now + , CourseParticipantField =. field + , CourseParticipantAllocated =. Nothing + , CourseParticipantState =. CourseParticipantActive + ] + queueDBJob . JobQueueNotification $ NotificationCourseRegistered uid cid + audit $ TransactionCourseParticipantEdit cid uid + addMessageIconI Success IconEnrolTrue MsgCourseRegisterOk + redirect currentRoute + _other -> error "Invalid @regButton@" + + mRegAt <- for (courseParticipantRegistration . entityVal <$> mRegistration) $ formatTime SelFormatDateTime + + return $(widgetFile "course/user/profile") + + +courseUserNoteSection :: Entity Course -> Entity User -> MaybeT Handler Widget +courseUserNoteSection (Entity cid Course{..}) (Entity uid _) = do + guardM . hasWriteAccessTo $ CourseR courseTerm courseSchool courseShorthand CUsersR + + currentRoute <- MaybeT getCurrentRoute + + (thisUniqueNote, noteText, noteEdits) <- lift . runDB $ do let thisUniqueNote = UniqueCourseUserNote uid cid mbNoteEnt <- getBy thisUniqueNote (noteText,noteEdits) <- case mbNoteEnt of @@ -51,13 +222,7 @@ postCUserR tid ssh csh uCId = do E.limit 1 -- more will be shown, if changed here return (edit E.^. CourseUserNoteEditTime, usr E.^. UserEmail, usr E.^. UserDisplayName, usr E.^. UserSurname) return (Just courseUserNoteNote, $(unValueN 4) <$> noteEdits) - -- Abfrage Studiengänge - studies <- E.select $ E.from $ \(studydegree `E.InnerJoin` studyfeat `E.InnerJoin` studyterms) -> do - E.where_ $ studyfeat E.^. StudyFeaturesUser E.==. E.val uid - E.on $ studyfeat E.^. StudyFeaturesField E.==. studyterms E.^. StudyTermsId - E.on $ studyfeat E.^. StudyFeaturesDegree E.==. studydegree E.^. StudyDegreeId - return (studyfeat, studydegree, studyterms) - return (cid, user, registration, thisUniqueNote, noteText, noteEdits, studies) + return (thisUniqueNote, noteText, noteEdits) let editByWgt = [whamlet| $newline never
_{MsgDBCsvDuplicateKey} -
_{MsgDBCsvDuplicateKeyTip}
- ^{offendingCsv}
+ $newline never
+ _{MsgDBCsvDuplicateKey}
+ _{MsgDBCsvDuplicateKeyTip}
+ ^{offendingCsv}
+ _{MsgDBCsvException}
- $if not (Text.null dbCsvException)
- #{dbCsvException}
- ^{ offendingCsv}
+ $newline never
+ _{MsgDBCsvException}
+ $if not (Text.null dbCsvException)
+ #{dbCsvException}
+ ^{offendingCsv}
+ _{MsgDBCsvParseErrorTip}
+
+ $case csvParseError
+ $of CsvParseError _ errMsg
+ #{errMsg}
+ $of IncrementalError errMsg
+ #{errMsg}
+