diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 4c18542ba..36c10837e 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -69,6 +69,11 @@ "type": "npm", "script": "lint", "problemMatcher": [] + }, + { + "type": "npm", + "script": "release", + "problemMatcher": [] } ] } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index dadeb24b1..6b2b815ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,97 @@ 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. +## [5.5.0](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/compare/v5.4.0...v5.5.0) (2019-08-27) + + +### Bug Fixes + +* **changelog:** add date ([52a88f8](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/commit/52a88f8)) +* **course-applications-csv:** record rating time ([c2c6974](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/commit/c2c6974)) + + +### Features + +* optional ribbon ([c2e13cf](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/commit/c2e13cf)) + + + +## [5.4.0](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/compare/v5.3.0...v5.4.0) (2019-08-27) + + +### Bug Fixes + +* **course-edit:** only show allocation error message when relevant ([00a6ca8](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/commit/00a6ca8)) + + +### Features + +* **allocations:** serve archive of all application files by course ([5e393c5](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/commit/5e393c5)) +* allow editing of course applications outside of allocation ([e816a30](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/commit/e816a30)) +* **course-applications:** csv transport ([cf0ec1a](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/commit/cf0ec1a)) + + + +## [5.3.0](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/compare/v5.2.3...v5.3.0) (2019-08-22) + + +### Bug Fixes + +* **allocations:** fix behaviour of "active" dbTable-filter ([b694a09](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/commit/b694a09)) +* **course list:** show complete registration span ([754d6ca](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/commit/754d6ca)), closes [#446](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/issues/446) +* **home:** fix hlint and other minor bugs ([839251e](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/commit/839251e)) + + +### Features + +* **allocations:** add info page for allocations ([689b85a](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/commit/689b85a)) +* **allocations:** show table of all allocations ([d621e61](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/commit/d621e61)) +* **allocations:** show table of course applications ([f5da3be](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/commit/f5da3be)) +* **home:** allow users to define exam warning time ([d23e222](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/commit/d23e222)), closes [#445](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/issues/445) +* **home:** clean up homepage ([a6e2f64](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/commit/a6e2f64)) + + + +### [5.2.3](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/compare/v5.2.2...v5.2.3) (2019-08-22) + + +### Bug Fixes + +* **csv exam import:** ignore unchanged noshow and voided ([a346524](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/commit/a346524)) + + + +### [5.2.2](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/compare/v5.2.1...v5.2.2) (2019-08-22) + + + +### [5.2.1](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/compare/v5.2.0...v5.2.1) (2019-08-21) + + +### Bug Fixes + +* **csv upload exams:** allow ambiguous harmless study fields ([7d2937c](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/commit/7d2937c)) + + + +## [5.2.0](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/compare/v5.1.0...v5.2.0) (2019-08-21) + + +### Bug Fixes + +* **csv import:** csv import preview help text adjusted ([b7321df](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/commit/b7321df)) +* **csv import:** fix spelling and expand help text ([2c57a77](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/commit/2c57a77)) +* **exam import:** inactive registered features may be selected ([3c4172c](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/commit/3c4172c)) +* **routes:** change ex to sheet ([9d9ead9](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/commit/9d9ead9)) +* **sheet list:** do not show icons for inaccessible items ([0bb9a0f](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/commit/0bb9a0f)), closes [#421](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/issues/421) + + +### Features + +* **csv import:** add explanation text ([6d0a4c1](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/commit/6d0a4c1)) + + + ## [5.1.0](https://gitlab.cip.ifi.lmu.de/jost/UniWorX/compare/v5.0.2...v5.1.0) (2019-08-19) diff --git a/config/settings.yml b/config/settings.yml index bcd9cabcb..ca2520708 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -119,5 +119,7 @@ user-defaults: date-format: "%d.%m.%Y" time-format: "%R" download-files: false + warning-days: 1209600 instance-id: "_env:INSTANCE_ID:instance" +ribbon: "_env:RIBBON:" diff --git a/frontend/src/utils/inputs/inputs.scss b/frontend/src/utils/inputs/inputs.scss index ae81b82d4..c4cb63373 100644 --- a/frontend/src/utils/inputs/inputs.scss +++ b/frontend/src/utils/inputs/inputs.scss @@ -1,6 +1,15 @@ /* GENERAL STYLES FOR FORMS */ /* FORM GROUPS */ +.form-section-title { + color: var(--color-fontsec); + margin: 0; + + + .form-group { + margin-top: 11px; + } +} + .form-group { position: relative; display: flex; @@ -19,15 +28,22 @@ } } -.form-section-title { - color: var(--color-fontsec); -} - .form-section-legend { color: var(--color-fontsec); margin: 7px 0; } +.form-section-title__hint { + margin-top: 7px; + color: var(--color-fontsec); + font-size: 0.9rem; + font-weight: 600; + + + .form-group { + margin-top: 11px; + } +} + .form-group-label { font-weight: 600; padding-top: 6px; diff --git a/messages/uniworx/de.msg b/messages/uniworx/de.msg index c4862d134..8568ce0e3 100644 --- a/messages/uniworx/de.msg +++ b/messages/uniworx/de.msg @@ -30,6 +30,7 @@ Aborted: Abgebrochen Remarks: Hinweise Registered: Angemeldet RegisteredSince: Angemeldet seit +Registration: Anmeldung RegisterFrom: Anmeldungen von RegisterTo: Anmeldungen bis DeRegUntil: Abmeldungen bis @@ -181,7 +182,7 @@ CourseApplicationRated: Bewertung erfolgreich angepasst CourseApplicationRatingDeleted: Bewertung erfolgreich entfernt CourseApplicationDeleted csh@CourseShorthand: Bewerbung zu #{csh} erfolgreich zurückgezogen -CourseApplicationTitle displayName@Text csh@CourseShorthand: Bewerbung für #{csh}: #{displayName} +CourseApplicationTitle displayName@Text csh@CourseShorthand: Bewerbung für #{csh}: #{displayName} CourseApplicationText: Text-Bewerbung CourseApplicationFollowInstructions: Beachten Sie die Anweisungen zur Bewerbung! @@ -190,7 +191,7 @@ CourseRegistrationFollowInstructions: Beachten Sie die Anweisungen zur Anmeldung CourseApplicationFile: Bewerbung CourseApplicationFiles: Bewerbungsdatei(en) -CourseApplicationArchive: Zip-Archiv der Bewerbungsdatei(en) +CourseApplicationArchive: Zip-Archiv der Bewerbungsdatei(en) CourseRegistrationFile: Datei zur Anmeldung CourseRegistrationFiles: Datei(en) zur Anmeldung CourseRegistrationArchive: Zip-Archiv der Datei(en) zur Anmeldung @@ -205,9 +206,12 @@ CourseLoginToApply: Um sich zum Kurz zu bewerben müssen Sie sich zunächst in U CourseLoginToRegister: Um sich zum Kurs anzumelden müssen Sie zunächst in Uni2work anmelden CourseApplicationArchiveName tid@TermId ssh@SchoolId csh@CourseShorthand appId@CryptoFileNameCourseApplication displayName@Text: #{foldCase (termToText (unTermKey tid))}-#{foldedCase (unSchoolKey ssh)}-#{foldedCase csh}-#{foldCase (toPathPiece appId)}-#{foldCase displayName} +CourseAllApplicationsArchiveName tid@TermId ssh@SchoolId csh@CourseShorthand: #{foldCase (termToText (unTermKey tid))}-#{foldedCase (unSchoolKey ssh)}-#{foldedCase csh}-bewerbungen +CourseApplicationsAllocatedDirectory: zentral +CourseApplicationsNotAllocatedDirectory: direkt CourseNoAllocationsAvailable: Es sind aktuell keine Zentralanmeldungen verfügbar -AllocationStaffRegisterToExpired: Es dürfen keine Änderungen an der Eintragung des Kurses zur Zentralanmeldung mehr vorgenommen werden +AllocationStaffRegisterToExpired: Es dürfen keine Änderungen an der Eintragung des Kurses zur Zentralanmeldung mehr vorgenommen werden. Ihre Änderungen wurden ignoriert. CourseFormSectionRegistration: Anmeldung zum Kurs @@ -226,6 +230,8 @@ CourseAllocationCapacityMayNotBeChanged: Kapazität eines Kurses, der an einer Z CourseLecturerRightsIdentical: Alle Sorten von Kursverwalter haben identische Rechte. +School: Institut + NoSuchTerm tid@TermId: Semester #{tid} gibt es nicht. NoSuchSchool ssh@SchoolId: Institut #{ssh} gibt es nicht. NoSuchCourseShorthand csh@CourseShorthand: Kein Kurs mit Kürzel #{csh} bekannt. @@ -450,7 +456,6 @@ NotificationSettingsHeading displayName@Text: Benachrichtigungs-Einstellungen f TokensLastReset: Tokens zuletzt invalidiert TokensResetSuccess: Authorisierungs-Tokens invalidiert -HomeOpenCourses: Kurse mit offener Registrierung HomeOpenAllocations: Offene Zentralanmeldungen HomeUpcomingSheets: Anstehende Übungsblätter HomeUpcomingExams: Bevorstehende Prüfungen @@ -594,7 +599,7 @@ MultiSinkException name@Text error@Text: In Abgabe #{name} ist ein Fehler aufget NoTableContent: Kein Tabelleninhalt NoUpcomingSheetDeadlines: Keine anstehenden Übungsblätter -NoUpcomingExams: In den nächsten 14 Tagen gibt es keine Prüfung mit offener Registrierung in Ihren Kursen +NoUpcomingExams difftime@Text: In den nächsten #{difftime} gibt es keine Prüfungen oder ablaufende Prüfungsanmeldungen in Ihren Kursen AdminHeading: Administration AdminUserHeading: Benutzeradministration @@ -620,6 +625,8 @@ DateFormat: Datumsformat TimeFormat: Uhrzeitformat DownloadFiles: Dateien automatisch herunterladen DownloadFilesTip: Wenn gesetzt werden Dateien von Abgaben und Übungsblättern automatisch als Download behandelt, ansonsten ist das Verhalten browserabhängig (es können z.B. PDFs im Browser geöffnet werden). +WarningDays: Fristen-Vorschau +WarningDaysTip: Wie viele Tage im Voraus sollen Fristen von Klausuren etc. auf Ihrer Startseite angezeigt werden? NotificationSettings: Erwünschte Benachrichtigungen FormNotifications: Benachrichtigungen FormBehaviour: Verhalten @@ -951,6 +958,8 @@ ErrorCryptoIdMismatch: Verschlüsselte Id der Abgabe passte nicht zu anderen Dat InvalidRoute: Konnte URL nicht interpretieren +MenuOpenCourses: Kurse mit offener Registrierung +MenuOpenAllocations: Aktive Zentralanmeldungen MenuHome: Aktuell MenuInformation: Informationen MenuImpressum: Impressum @@ -962,10 +971,12 @@ MenuHelp: Hilfe MenuProfile: Anpassen MenuLogin: Login MenuLogout: Logout +MenuAllocationList: Zentralanmeldungen MenuCourseList: Kurse MenuCourseMembers: Kursteilnehmer MenuCourseAddMembers: Kursteilnehmer hinzufügen MenuCourseCommunication: Kursmitteilung +MenuCourseApplications: Bewerbungen MenuTermShow: Semester MenuSubmissionDelete: Abgabe löschen MenuUsers: Benutzer @@ -1018,6 +1029,8 @@ MenuExamEdit: Bearbeiten MenuExamUsers: Teilnehmer MenuExamAddMembers: Prüfungsteilnehmer hinzufügen MenuLecturerInvite: Dozenten hinzufügen +MenuAllocationInfo: Hinweise zum Ablauf einer Zentralanmeldung +MenuCourseApplicationsFiles: Dateien aller Bewerbungen AuthPredsInfo: Um eigene Veranstaltungen aus Sicht der Teilnehmer anzusehen, können Veranstalter und Korrektoren hier die Prüfung ihrer erweiterten Berechtigungen temporär deaktivieren. Abgewählte Prädikate schlagen immer fehl. Abgewählte Prädikate werden also nicht geprüft um Zugriffe zu gewähren, welche andernfalls nicht erlaubt wären. Diese Einstellungen gelten nur temporär bis Ihre Sitzung abgelaufen ist, d.h. bis ihr Browser-Cookie abgelaufen ist. Durch Abwahl von Prädikaten kann man sich höchstens temporär aussperren. AuthPredsActive: Aktive Authorisierungsprädikate @@ -1373,11 +1386,12 @@ BtnCsvImport: CSV-Datei importieren BtnCsvImportConfirm: CSV-Import abschließen CsvImportNotConfigured: CSV-Import nicht vorgesehen -CsvImportConfirmationHeading: CSV-Import abschließen -CsvImportConfirmationTip: Durch den CSV-Import würden die unten aufgeführten Änderungen vorgenommen. Bitte überprüfen Sie diese zunächst sorgfältig. +CsvImportConfirmationHeading: CSV-Import Vorschau (noch keine Änderungen importiert) +CsvImportConfirmationTip: Es wurden noch keine Änderungen übernommen! Durch den CSV-Import könnten die unten aufgeführten Änderungen vorgenommen werden. Wählen Sie jetzt die gewünschten Änderungen aus, bevor Sie den CSV-Import abschließen. CsvImportUnnecessary: Durch den CSV-Import würden keine Änderungen vorgenommen werden 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 Proportion c@Text of@Text prop@Rational: #{c}/#{of} (#{rationalToFixed2 (100 * prop)}%) @@ -1397,6 +1411,18 @@ CsvColumnExamUserExercisePasses: Anzahl von Übungsblättern, die der Teilnehmer CsvColumnExamUserExercisePassesMax: Maximale Anzahl von Übungsblättern, die der Teilnehmer bis zu seinem Prüfungstermin bestehen hätte können CsvColumnExamUserResult: Erreichte Prüfungsleistung; "passed", "failed", "no-show", "voided", oder eine Note ("1.0", "1.3", "1.7", ..., "4.0", "5.0") CsvColumnExamUserCourseNote: Notizen zum Teilnehmer +CsvColumnApplicationsAllocation: Zentralanmeldung über die die Bewerbung eingegangen ist +CsvColumnApplicationsApplication: Eindeutige Nummer der Bewerbung (zur Zuordnung im ZIP-Archiv aller Bewerbungsdateien) +CsvColumnApplicationsName: Voller Name des Bewerbers +CsvColumnApplicationsMatriculation: Matrikelnummer des Bewerbers +CsvColumnApplicationsField: Studienfach, mit dem der Bewerber seine Bewerbung assoziiert hat +CsvColumnApplicationsDegree: Abschluss, den der Bewerber im assoziierten Studienfach anstrebt +CsvColumnApplicationsSemester: Fachsemester des Bewerbes im assoziierten Studienfach +CsvColumnApplicationsText: Text-Bewerbung +CsvColumnApplicationsHasFiles: Hat der Bewerber Dateien zu seiner Bewerbung eingereicht (siehe ZIP-Archiv aller Bewerbungsdateien)? +CsvColumnApplicationsVeto: Bewerber mit Veto werden garantiert nicht dem Kurs zugeteilt; "veto" oder leer +CsvColumnApplicationsRating: Bewertung der Bewerbung; "1.0", "1.3", "1.7", ..., "4.0", "5.0" +CsvColumnApplicationsComment: Kommentar zur Bewerbung; je nach Kurs-Einstellungen entweder nur als Notiz für die Kursverwalter oder Feedback für den Bewerber Action: Aktion @@ -1419,6 +1445,15 @@ ExamUserCsvExceptionNoMatchingUser: Kursteilnehmer konnte nicht eindeutig identi ExamUserCsvExceptionNoMatchingStudyFeatures: Das angegebene Studienfach konnte keinem Studienfach des Kursteilnehmers zugeordnet werden ExamUserCsvExceptionNoMatchingOccurrence: Raum/Termin konnte nicht eindeutig identifiziert werden +CourseApplicationsTableCsvSetField: Bewerbungs-assoziiertes Studienfach ändern +CourseApplicationsTableCsvSetVeto: Veto setzen/entfernen +CourseApplicationsTableCsvSetRating: Bewertung eintragen +CourseApplicationsTableCsvSetComment: Bewertungskommentar eintragen + +CourseApplicationsTableCsvExceptionNoMatchingUser: Bewerber konnte nicht eindeutig identifiziert werden +CourseApplicationsTableCsvExceptionNoMatchingAllocation: Zentralanmeldung konnte nicht eindeutig identifiziert werden +CourseApplicationsTableCsvExceptionNoMatchingStudyFeatures: Das angegebene Studienfach konnte keinem Studienfach des Kursteilnehmers zugeordnet werden + TableHeadingFilter: Filter TableHeadingCsvImport: CSV-Import TableHeadingCsvExport: CSV-Export @@ -1466,6 +1501,8 @@ MailSchoolLecturerInviteHeading school@SchoolName: Einladung zum Dozent für „ SchoolLecturerInviteExplanation: Sie wurden eingeladen, Dozent für ein Institut zu sein. Sie können, nachdem Sie die Einladung annehmen, eigenständig neue Kurse anlegen. SchoolLecturerInvitationAccepted school@SchoolName: Einladung zum Dozent für „#{school}“ angenommen +AllocationActive: Aktiv +AllocationName: Name AllocationTitle termText@Text ssh'@SchoolShorthand allocation@AllocationName: #{termText} - #{ssh'}: #{allocation} AllocationShortTitle termText@Text ssh'@SchoolShorthand ash@AllocationShorthand: #{termText} - #{ssh'} - #{ash} AllocationDescription: Beschreibung @@ -1473,8 +1510,11 @@ AllocationStaffRegisterFrom: Eintragung der Kurse ab AllocationStaffRegister: Eintragung der Kurse AllocationRegisterFrom: Bewerbung ab AllocationRegister: Bewerbung +AllocationRegisterClosed: Die Zentralanmeldung ist aktuell geschlossen. +AllocationRegisterOpensIn difftime@Text: Die Zentralanmeldung öffnet voraussichtlich in #{difftime} AllocationStaffAllocationFrom: Bewertung der Bewerbungen ab -AllocationStaffAllocation: Bewertung der Bewerbungen +AllocationStaffAllocation: Bewerbungsbewertung +AllocationProcess: Platzvergabe AllocationNoApplication: Keine Bewerbung AllocationPriority: Priorität AllocationPriorityTip: Kurse, denen Sie eine höhere Priorität zuteilen, werden bei der Platzvergabe präferiert. @@ -1488,7 +1528,7 @@ BtnAllocationRegister: Teilnahme registrieren BtnAllocationRegistrationEdit: Teilnahme anpassen AllocationParticipation: Teilnahme an der Zentralanmeldung AllocationParticipationLoginFirst: Um an der Zentralanmeldung teilzunehmen, loggen Sie sich bitte zunächst ein. -AllocationCourses: Kurse +AllocationCourses: Kurse dieser Zentralanmeldung AllocationData: Organisatorisches AllocationCoursePriority i@Natural: #{i}. Wahl AllocationCourseNoApplication: Keine Bewerbung @@ -1504,7 +1544,22 @@ ApplicationRatingPointsTip: Bewerber mit 5.0 werden garantiert nicht dem Kurs zu ApplicationRatingComment: Kommentar ApplicationRatingCommentVisibleTip: Feedback an den Bewerbers ApplicationRatingCommentInvisibleTip: Dient zunächst nur als Notiz für Kursverwalter +ApplicationRatingSection: Bewertung +ApplicationRatingSectionSelfTip: Sie verfügen über hinreichende Authorisierung um sowohl die Bewerbung als auch ihre Bewertung zu editieren. AllocationSchoolShort: Institut Allocation: Zentralanmeldung -AllocationRegisterTo: Anmeldungen bis \ No newline at end of file +AllocationRegisterTo: Anmeldungen bis + +AllocationListTitle: Zentralanmeldungen + +CourseApplicationsListTitle: Bewerbungen +CourseApplicationId: Bewerbungsnummer +CourseApplicationRatingPoints: Bewertung +CourseApplicationVeto: Veto +CourseApplicationNoVeto: Kein Veto +CourseApplicationNoRatingPoints: Keine Bewertung +CourseApplicationNoRatingComment: Kein Kommentar + +UserDisplayName: Voller Name +UserMatriculation: Matrikelnummer \ No newline at end of file diff --git a/models/allocations b/models/allocations index 0fac2cfee..9ddbd59bd 100644 --- a/models/allocations +++ b/models/allocations @@ -1,8 +1,8 @@ Allocation -- attributes with prefix staff- affect lecturers only, but are invisble to students - name AllocationName - shorthand AllocationShorthand -- practical shorthand term TermId school SchoolId -- school that manages this central allocation, not necessarily school of courses + shorthand AllocationShorthand -- practical shorthand + name AllocationName description Html Maybe -- description for prospective students staffDescription Html Maybe -- description seen by prospective lecturers only staffRegisterFrom UTCTime Maybe -- lectureres may register courses @@ -23,7 +23,7 @@ Allocation -- attributes with prefix staff- affect lecturers only, but are invis -- overrideVisible not needed, since courses are always visible 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 + deriving Show Eq Ord Generic AllocationCourse allocation AllocationId diff --git a/models/users b/models/users index 33a92adf1..f66651dd5 100644 --- a/models/users +++ b/models/users @@ -8,14 +8,14 @@ -- Each table will also have an column storing a unique numeric row key, unless there is a row Primary columnname -- User json -- Each Uni2work user has a corresponding row in this table; created upon first login. + surname UserSurname -- Display user names always through 'nameWidget displayName surname' + displayName UserDisplayName -- we only show LDAP-DisplayName, and highlight LDAP-Surname within (appended if not contained) + email (CI Text) -- Case-insensitive eMail address ident (CI Text) -- Case-insensitive user-identifier authentication AuthenticationMode -- 'AuthLDAP' or ('AuthPWHash'+password-hash) lastAuthentication UTCTime Maybe -- last login date - tokensIssuedAfter UTCTime Maybe -- do not accept bearer tokens issued before this time (accept all tokens if null) - matrikelnummer Text Maybe -- optional immatriculation-string; usually a number, but not always (e.g. lecturers, pupils, guests,...) - email (CI Text) -- Case-insensitive eMail address - displayName Text -- we only show LDAP-DisplayName, and highlight LDAP-Surname within (appended if not contained) - surname Text -- Display user names always through 'nameWidget displayName surname' + tokensIssuedAfter UTCTime Maybe -- do not accept bearer tokens issued before this time (accept all tokens if null) + matrikelnummer UserMatriculation Maybe -- optional immatriculation-string; usually a number, but not always (e.g. lecturers, pupils, guests,...) firstName Text -- For export in tables, pre-split firstName from displayName title Text Maybe -- For upcoming name customisation maxFavourites Int default=12 -- max number of rows with this userId in table "CourseFavourite"; for convenience links; user-defined @@ -23,12 +23,13 @@ User json -- Each Uni2work user has a corresponding row in this table; create dateTimeFormat DateTimeFormat "default='%a %d %b %Y %R'" -- preferred Date+Time display format for user; user-defined dateFormat DateTimeFormat "default='%d.%m.%Y'" -- preferred Date-only display format for user; user-defined timeFormat DateTimeFormat "default='%R'" -- preferred Time-only display format for user; user-defined - downloadFiles Bool default=false -- Should files be opened in browser or downloaded? (users often oblivious that their browser has a setting for this) + downloadFiles Bool default=false -- Should files be opened in browser or downloaded? (users often oblivious that their browser has a setting for this) mailLanguages MailLanguages "default='[]'::jsonb" -- Preferred language for eMail; i18n not yet implemented; user-defined - notificationSettings NotificationSettings -- Bit-array for which events email notifications are requested by user; user-defined - UniqueAuthentication ident -- Column 'ident' can be used as a row-key in this table - UniqueEmail email -- Column 'email' can be used as a row-key in this table - deriving Show Eq Generic -- Haskell-specific settings for runtime-value representing a row in memory + notificationSettings NotificationSettings -- Bit-array for which events email notifications are requested by user; user-defined + warningDays NominalDiffTime default=1209600 -- timedistance to pending deadlines for homepage infos + UniqueAuthentication ident -- Column 'ident' can be used as a row-key in this table + UniqueEmail email -- Column 'email' can be used as a row-key in this table + deriving Show Eq Ord Generic -- Haskell-specific settings for runtime-value representing a row in memory UserAdmin -- Each row in this table grants school-specific administrator-rights to a specific user user UserId school SchoolId diff --git a/package-lock.json b/package-lock.json index 183967506..919243060 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "uni2work", - "version": "5.1.0", + "version": "5.5.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -5956,10 +5956,13 @@ } }, "eslint-utils": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", - "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", - "dev": true + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz", + "integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.0.0" + } }, "eslint-visitor-keys": { "version": "1.0.0", @@ -6531,14 +6534,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6558,8 +6559,7 @@ "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", @@ -6707,7 +6707,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -6715,8 +6714,7 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", @@ -9906,7 +9904,7 @@ "dependencies": { "JSONStream": { "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "resolved": false, "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", "requires": { "jsonparse": "^1.2.0", @@ -9915,7 +9913,7 @@ }, "abbrev": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "resolved": false, "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "agent-base": { @@ -9936,7 +9934,7 @@ }, "ajv": { "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "resolved": false, "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", "requires": { "co": "^4.6.0", @@ -9947,7 +9945,7 @@ }, "ansi-align": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", + "resolved": false, "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", "requires": { "string-width": "^2.0.0" @@ -9955,12 +9953,12 @@ }, "ansi-regex": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "resolved": false, "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "ansi-styles": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "resolved": false, "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "requires": { "color-convert": "^1.9.0" @@ -9968,27 +9966,27 @@ }, "ansicolors": { "version": "0.3.2", - "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", + "resolved": false, "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=" }, "ansistyles": { "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ansistyles/-/ansistyles-0.1.3.tgz", + "resolved": false, "integrity": "sha1-XeYEFb2gcbs3EnhUyGT0GyMlRTk=" }, "aproba": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "resolved": false, "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" }, "archy": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "resolved": false, "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" }, "are-we-there-yet": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", + "resolved": false, "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", "requires": { "delegates": "^1.0.0", @@ -9997,7 +9995,7 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": false, "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -10011,7 +10009,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -10021,12 +10019,12 @@ }, "asap": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "resolved": false, "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" }, "asn1": { "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "resolved": false, "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", "requires": { "safer-buffer": "~2.1.0" @@ -10034,32 +10032,32 @@ }, "assert-plus": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "resolved": false, "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, "asynckit": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "resolved": false, "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "aws-sign2": { "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "resolved": false, "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "resolved": false, "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, "balanced-match": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "resolved": false, "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "bcrypt-pbkdf": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "resolved": false, "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "optional": true, "requires": { @@ -10068,7 +10066,7 @@ }, "bin-links": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-1.1.2.tgz", + "resolved": false, "integrity": "sha512-8eEHVgYP03nILphilltWjeIjMbKyJo3wvp9K816pHbhP301ismzw15mxAAEVQ/USUwcP++1uNrbERbp8lOA6Fg==", "requires": { "bluebird": "^3.5.0", @@ -10080,12 +10078,12 @@ }, "bluebird": { "version": "3.5.5", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", + "resolved": false, "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==" }, "boxen": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", + "resolved": false, "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", "requires": { "ansi-align": "^2.0.0", @@ -10099,7 +10097,7 @@ }, "brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "requires": { "balanced-match": "^1.0.0", @@ -10108,22 +10106,22 @@ }, "buffer-from": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz", + "resolved": false, "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==" }, "builtins": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "resolved": false, "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=" }, "byline": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", + "resolved": false, "integrity": "sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE=" }, "byte-size": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/byte-size/-/byte-size-5.0.1.tgz", + "resolved": false, "integrity": "sha512-/XuKeqWocKsYa/cBY1YbSJSWWqTi4cFgr9S6OyM7PBaPbr9zvNGwWP33vt0uqGhwDdN+y3yhbXVILEUpnwEWGw==" }, "cacache": { @@ -10150,27 +10148,27 @@ }, "call-limit": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/call-limit/-/call-limit-1.1.1.tgz", + "resolved": false, "integrity": "sha512-5twvci5b9eRBw2wCfPtN0GmlR2/gadZqyFpPhOK6CvMFoFgA+USnZ6Jpu1lhG9h85pQ3Ouil3PfXWRD4EUaRiQ==" }, "camelcase": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "resolved": false, "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" }, "capture-stack-trace": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz", + "resolved": false, "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=" }, "caseless": { "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "resolved": false, "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "chalk": { "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "resolved": false, "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "requires": { "ansi-styles": "^3.2.1", @@ -10180,17 +10178,17 @@ }, "chownr": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.2.tgz", + "resolved": false, "integrity": "sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A==" }, "ci-info": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "resolved": false, "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" }, "cidr-regex": { "version": "2.0.10", - "resolved": "https://registry.npmjs.org/cidr-regex/-/cidr-regex-2.0.10.tgz", + "resolved": false, "integrity": "sha512-sB3ogMQXWvreNPbJUZMRApxuRYd+KoIo4RGQ81VatjmMW6WJPo+IJZ2846FGItr9VzKo5w7DXzijPLGtSd0N3Q==", "requires": { "ip-regex": "^2.1.0" @@ -10198,12 +10196,12 @@ }, "cli-boxes": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", + "resolved": false, "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=" }, "cli-columns": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/cli-columns/-/cli-columns-3.1.2.tgz", + "resolved": false, "integrity": "sha1-ZzLZcpee/CrkRKHwjgj6E5yWoY4=", "requires": { "string-width": "^2.0.0", @@ -10212,7 +10210,7 @@ }, "cli-table3": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz", + "resolved": false, "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==", "requires": { "colors": "^1.1.2", @@ -10222,7 +10220,7 @@ }, "cliui": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "resolved": false, "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", "requires": { "string-width": "^2.1.1", @@ -10232,12 +10230,12 @@ "dependencies": { "ansi-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "resolved": false, "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" }, "strip-ansi": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "resolved": false, "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "requires": { "ansi-regex": "^3.0.0" @@ -10247,12 +10245,12 @@ }, "clone": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "resolved": false, "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=" }, "cmd-shim": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-2.0.2.tgz", + "resolved": false, "integrity": "sha1-b8vamUg6j9FdfTChlspp1oii79s=", "requires": { "graceful-fs": "^4.1.2", @@ -10261,17 +10259,17 @@ }, "co": { "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "resolved": false, "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" }, "code-point-at": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "resolved": false, "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "color-convert": { "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "resolved": false, "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", "requires": { "color-name": "^1.1.1" @@ -10279,18 +10277,18 @@ }, "color-name": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "resolved": false, "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "colors": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", + "resolved": false, "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==", "optional": true }, "columnify": { "version": "1.5.4", - "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.5.4.tgz", + "resolved": false, "integrity": "sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs=", "requires": { "strip-ansi": "^3.0.0", @@ -10299,7 +10297,7 @@ }, "combined-stream": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "resolved": false, "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", "requires": { "delayed-stream": "~1.0.0" @@ -10307,12 +10305,12 @@ }, "concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "resolved": false, "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "concat-stream": { "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "resolved": false, "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "requires": { "buffer-from": "^1.0.0", @@ -10323,7 +10321,7 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": false, "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -10337,7 +10335,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -10347,7 +10345,7 @@ }, "config-chain": { "version": "1.1.12", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", + "resolved": false, "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", "requires": { "ini": "^1.3.4", @@ -10356,7 +10354,7 @@ }, "configstore": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", + "resolved": false, "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", "requires": { "dot-prop": "^4.1.0", @@ -10369,12 +10367,12 @@ }, "console-control-strings": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "resolved": false, "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, "copy-concurrently": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "resolved": false, "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", "requires": { "aproba": "^1.1.1", @@ -10387,24 +10385,24 @@ "dependencies": { "aproba": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "resolved": false, "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" }, "iferr": { "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "resolved": false, "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" } } }, "core-util-is": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "resolved": false, "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "create-error-class": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", + "resolved": false, "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", "requires": { "capture-stack-trace": "^1.0.0" @@ -10412,7 +10410,7 @@ }, "cross-spawn": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "resolved": false, "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "requires": { "lru-cache": "^4.0.1", @@ -10422,7 +10420,7 @@ "dependencies": { "lru-cache": { "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "resolved": false, "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", "requires": { "pseudomap": "^1.0.2", @@ -10431,24 +10429,24 @@ }, "yallist": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "resolved": false, "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" } } }, "crypto-random-string": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", + "resolved": false, "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=" }, "cyclist": { "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", + "resolved": false, "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=" }, "dashdash": { "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "resolved": false, "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "requires": { "assert-plus": "^1.0.0" @@ -10456,7 +10454,7 @@ }, "debug": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "resolved": false, "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "requires": { "ms": "2.0.0" @@ -10464,34 +10462,34 @@ "dependencies": { "ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "resolved": false, "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, "debuglog": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", + "resolved": false, "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=" }, "decamelize": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "resolved": false, "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, "decode-uri-component": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "resolved": false, "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" }, "deep-extend": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz", + "resolved": false, "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==" }, "defaults": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "resolved": false, "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", "requires": { "clone": "^1.0.2" @@ -10499,7 +10497,7 @@ }, "define-properties": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "resolved": false, "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "requires": { "object-keys": "^1.0.12" @@ -10507,27 +10505,27 @@ }, "delayed-stream": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "resolved": false, "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "delegates": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "resolved": false, "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" }, "detect-indent": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz", + "resolved": false, "integrity": "sha1-OHHMCmoALow+Wzz38zYmRnXwa50=" }, "detect-newline": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "resolved": false, "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=" }, "dezalgo": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", + "resolved": false, "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=", "requires": { "asap": "^2.0.0", @@ -10536,7 +10534,7 @@ }, "dot-prop": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", + "resolved": false, "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", "requires": { "is-obj": "^1.0.0" @@ -10544,17 +10542,17 @@ }, "dotenv": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-5.0.1.tgz", + "resolved": false, "integrity": "sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow==" }, "duplexer3": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "resolved": false, "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" }, "duplexify": { "version": "3.6.0", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz", + "resolved": false, "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", "requires": { "end-of-stream": "^1.0.0", @@ -10565,7 +10563,7 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": false, "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -10579,7 +10577,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -10589,7 +10587,7 @@ }, "ecc-jsbn": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "resolved": false, "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "optional": true, "requires": { @@ -10599,12 +10597,12 @@ }, "editor": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/editor/-/editor-1.0.0.tgz", + "resolved": false, "integrity": "sha1-YMf4e9YrzGqJT6jM1q+3gjok90I=" }, "encoding": { "version": "0.1.12", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "resolved": false, "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", "requires": { "iconv-lite": "~0.4.13" @@ -10612,7 +10610,7 @@ }, "end-of-stream": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "resolved": false, "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", "requires": { "once": "^1.4.0" @@ -10620,17 +10618,17 @@ }, "env-paths": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-1.0.0.tgz", + "resolved": false, "integrity": "sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA=" }, "err-code": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", + "resolved": false, "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=" }, "errno": { "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "resolved": false, "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", "requires": { "prr": "~1.0.1" @@ -10638,7 +10636,7 @@ }, "es-abstract": { "version": "1.12.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz", + "resolved": false, "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", "requires": { "es-to-primitive": "^1.1.1", @@ -10650,7 +10648,7 @@ }, "es-to-primitive": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "resolved": false, "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", "requires": { "is-callable": "^1.1.4", @@ -10665,7 +10663,7 @@ }, "es6-promisify": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "resolved": false, "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", "requires": { "es6-promise": "^4.0.3" @@ -10673,12 +10671,12 @@ }, "escape-string-regexp": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "resolved": false, "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "execa": { "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "resolved": false, "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "requires": { "cross-spawn": "^5.0.1", @@ -10692,44 +10690,44 @@ "dependencies": { "get-stream": { "version": "3.0.0", - "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": false, "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" } } }, "extend": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "resolved": false, "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "extsprintf": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "resolved": false, "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, "fast-deep-equal": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "resolved": false, "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" }, "fast-json-stable-stringify": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "resolved": false, "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" }, "figgy-pudding": { "version": "3.5.1", - "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", + "resolved": false, "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==" }, "find-npm-prefix": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/find-npm-prefix/-/find-npm-prefix-1.0.2.tgz", + "resolved": false, "integrity": "sha512-KEftzJ+H90x6pcKtdXZEPsQse8/y/UnvzRKrOSQFprnrGaFuJ62fVkP34Iu2IYuMvyauCyoLTNkJZgrrGA2wkA==" }, "find-up": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "resolved": false, "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "requires": { "locate-path": "^2.0.0" @@ -10737,7 +10735,7 @@ }, "flush-write-stream": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz", + "resolved": false, "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", "requires": { "inherits": "^2.0.1", @@ -10746,7 +10744,7 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": false, "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -10760,7 +10758,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -10770,12 +10768,12 @@ }, "forever-agent": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "resolved": false, "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, "form-data": { "version": "2.3.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", + "resolved": false, "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", "requires": { "asynckit": "^0.4.0", @@ -10785,7 +10783,7 @@ }, "from2": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "resolved": false, "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", "requires": { "inherits": "^2.0.1", @@ -10794,7 +10792,7 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": false, "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -10808,7 +10806,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -10818,7 +10816,7 @@ }, "fs-minipass": { "version": "1.2.6", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.6.tgz", + "resolved": false, "integrity": "sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ==", "requires": { "minipass": "^2.2.1" @@ -10826,7 +10824,7 @@ }, "fs-vacuum": { "version": "1.2.10", - "resolved": "https://registry.npmjs.org/fs-vacuum/-/fs-vacuum-1.2.10.tgz", + "resolved": false, "integrity": "sha1-t2Kb7AekAxolSP35n17PHMizHjY=", "requires": { "graceful-fs": "^4.1.2", @@ -10836,7 +10834,7 @@ }, "fs-write-stream-atomic": { "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "resolved": false, "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", "requires": { "graceful-fs": "^4.1.2", @@ -10847,12 +10845,12 @@ "dependencies": { "iferr": { "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "resolved": false, "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": false, "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -10866,7 +10864,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -10876,17 +10874,17 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "resolved": false, "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "function-bind": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "resolved": false, "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "gauge": { "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "resolved": false, "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "requires": { "aproba": "^1.0.3", @@ -10901,12 +10899,12 @@ "dependencies": { "aproba": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "resolved": false, "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" }, "string-width": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { "code-point-at": "^1.0.0", @@ -10918,12 +10916,12 @@ }, "genfun": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz", + "resolved": false, "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==" }, "gentle-fs": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/gentle-fs/-/gentle-fs-2.0.1.tgz", + "resolved": false, "integrity": "sha512-cEng5+3fuARewXktTEGbwsktcldA+YsnUEaXZwcK/3pjSE1X9ObnTs+/8rYf8s+RnIcQm2D5x3rwpN7Zom8Bew==", "requires": { "aproba": "^1.1.2", @@ -10938,24 +10936,24 @@ "dependencies": { "aproba": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "resolved": false, "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" }, "iferr": { "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "resolved": false, "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" } } }, "get-caller-file": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", + "resolved": false, "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=" }, "get-stream": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "resolved": false, "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "requires": { "pump": "^3.0.0" @@ -10963,7 +10961,7 @@ }, "getpass": { "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "resolved": false, "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "requires": { "assert-plus": "^1.0.0" @@ -10971,7 +10969,7 @@ }, "glob": { "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "resolved": false, "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", "requires": { "fs.realpath": "^1.0.0", @@ -10984,7 +10982,7 @@ }, "global-dirs": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "resolved": false, "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", "requires": { "ini": "^1.3.4" @@ -10992,7 +10990,7 @@ }, "got": { "version": "6.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", + "resolved": false, "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", "requires": { "create-error-class": "^3.0.0", @@ -11010,24 +11008,24 @@ "dependencies": { "get-stream": { "version": "3.0.0", - "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": false, "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" } } }, "graceful-fs": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.0.tgz", + "resolved": false, "integrity": "sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg==" }, "har-schema": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "resolved": false, "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" }, "har-validator": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz", + "resolved": false, "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", "requires": { "ajv": "^5.3.0", @@ -11036,7 +11034,7 @@ }, "has": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "resolved": false, "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "requires": { "function-bind": "^1.1.1" @@ -11044,17 +11042,17 @@ }, "has-flag": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "resolved": false, "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "has-symbols": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "resolved": false, "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" }, "has-unicode": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "resolved": false, "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" }, "hosted-git-info": { @@ -11067,12 +11065,12 @@ }, "http-cache-semantics": { "version": "3.8.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", + "resolved": false, "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==" }, "http-proxy-agent": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", + "resolved": false, "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", "requires": { "agent-base": "4", @@ -11081,7 +11079,7 @@ }, "http-signature": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "resolved": false, "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "requires": { "assert-plus": "^1.0.0", @@ -11100,7 +11098,7 @@ }, "humanize-ms": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "resolved": false, "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", "requires": { "ms": "^2.0.0" @@ -11108,7 +11106,7 @@ }, "iconv-lite": { "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "resolved": false, "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", "requires": { "safer-buffer": ">= 2.1.2 < 3" @@ -11116,12 +11114,12 @@ }, "iferr": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-1.0.2.tgz", + "resolved": false, "integrity": "sha512-9AfeLfji44r5TKInjhz3W9DyZI1zR1JAf2hVBMGhddAKPqBsupb89jGfbCTHIGZd6fGZl9WlHdn4AObygyMKwg==" }, "ignore-walk": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "resolved": false, "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "requires": { "minimatch": "^3.0.4" @@ -11129,12 +11127,12 @@ }, "import-lazy": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "resolved": false, "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" }, "imurmurhash": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "resolved": false, "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" }, "infer-owner": { @@ -11144,7 +11142,7 @@ }, "inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "resolved": false, "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "requires": { "once": "^1.3.0", @@ -11153,17 +11151,17 @@ }, "inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "resolved": false, "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ini": { "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "resolved": false, "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" }, "init-package-json": { "version": "1.10.3", - "resolved": "https://registry.npmjs.org/init-package-json/-/init-package-json-1.10.3.tgz", + "resolved": false, "integrity": "sha512-zKSiXKhQveNteyhcj1CoOP8tqp1QuxPIPBl8Bid99DGLFqA1p87M6lNgfjJHSBoWJJlidGOv5rWjyYKEB3g2Jw==", "requires": { "glob": "^7.1.1", @@ -11178,27 +11176,27 @@ }, "invert-kv": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "resolved": false, "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" }, "ip": { "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "resolved": false, "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" }, "ip-regex": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "resolved": false, "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" }, "is-callable": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "resolved": false, "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" }, "is-ci": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.1.0.tgz", + "resolved": false, "integrity": "sha512-c7TnwxLePuqIlxHgr7xtxzycJPegNHFuIrBkwbf8hc58//+Op1CqFkyS+xnIMkwn9UsJIwc174BIjkyBmSpjKg==", "requires": { "ci-info": "^1.0.0" @@ -11206,14 +11204,14 @@ "dependencies": { "ci-info": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", + "resolved": false, "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==" } } }, "is-cidr": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-cidr/-/is-cidr-3.0.0.tgz", + "resolved": false, "integrity": "sha512-8Xnnbjsb0x462VoYiGlhEi+drY8SFwrHiSYuzc/CEwco55vkehTaxAyIjEdpi3EMvLPPJAJi9FlzP+h+03gp0Q==", "requires": { "cidr-regex": "^2.0.10" @@ -11221,12 +11219,12 @@ }, "is-date-object": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "resolved": false, "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "requires": { "number-is-nan": "^1.0.0" @@ -11234,7 +11232,7 @@ }, "is-installed-globally": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", + "resolved": false, "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", "requires": { "global-dirs": "^0.1.0", @@ -11243,17 +11241,17 @@ }, "is-npm": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", + "resolved": false, "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=" }, "is-obj": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "resolved": false, "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" }, "is-path-inside": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "resolved": false, "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", "requires": { "path-is-inside": "^1.0.1" @@ -11261,12 +11259,12 @@ }, "is-redirect": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", + "resolved": false, "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=" }, "is-regex": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "resolved": false, "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", "requires": { "has": "^1.0.1" @@ -11274,17 +11272,17 @@ }, "is-retry-allowed": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", + "resolved": false, "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=" }, "is-stream": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "resolved": false, "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, "is-symbol": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "resolved": false, "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", "requires": { "has-symbols": "^1.0.0" @@ -11292,58 +11290,58 @@ }, "is-typedarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "resolved": false, "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, "isarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "resolved": false, "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "resolved": false, "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "isstream": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "resolved": false, "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, "jsbn": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "resolved": false, "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "optional": true }, "json-parse-better-errors": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "resolved": false, "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" }, "json-schema": { "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "resolved": false, "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" }, "json-schema-traverse": { "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "resolved": false, "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" }, "json-stringify-safe": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "resolved": false, "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, "jsonparse": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "resolved": false, "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" }, "jsprim": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "resolved": false, "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", "requires": { "assert-plus": "1.0.0", @@ -11354,7 +11352,7 @@ }, "latest-version": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", + "resolved": false, "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", "requires": { "package-json": "^4.0.0" @@ -11362,12 +11360,12 @@ }, "lazy-property": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lazy-property/-/lazy-property-1.0.0.tgz", + "resolved": false, "integrity": "sha1-hN3Es3Bnm6i9TNz6TAa0PVcREUc=" }, "lcid": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "resolved": false, "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "requires": { "invert-kv": "^1.0.0" @@ -11375,7 +11373,7 @@ }, "libcipm": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/libcipm/-/libcipm-4.0.0.tgz", + "resolved": false, "integrity": "sha512-5IIamvUIqWYjfNscYdirKisXyaTMw7Mf7yuGrjHH2isz7xBZDCUOIdujZxNk2g6lBBs8AGxYW6lHpNnnt92bww==", "requires": { "bin-links": "^1.1.2", @@ -11435,7 +11433,7 @@ }, "libnpmconfig": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/libnpmconfig/-/libnpmconfig-1.2.1.tgz", + "resolved": false, "integrity": "sha512-9esX8rTQAHqarx6qeZqmGQKBNZR5OIbl/Ayr0qQDy3oXja2iFVQQI81R6GZ2a02bSNZ9p3YOGX1O6HHCb1X7kA==", "requires": { "figgy-pudding": "^3.5.1", @@ -11445,7 +11443,7 @@ "dependencies": { "find-up": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "resolved": false, "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "requires": { "locate-path": "^3.0.0" @@ -11453,7 +11451,7 @@ }, "locate-path": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "resolved": false, "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "requires": { "p-locate": "^3.0.0", @@ -11462,7 +11460,7 @@ }, "p-limit": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "resolved": false, "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", "requires": { "p-try": "^2.0.0" @@ -11470,7 +11468,7 @@ }, "p-locate": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "resolved": false, "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "requires": { "p-limit": "^2.0.0" @@ -11478,7 +11476,7 @@ }, "p-try": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "resolved": false, "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" } } @@ -11544,7 +11542,7 @@ }, "libnpx": { "version": "10.2.0", - "resolved": "https://registry.npmjs.org/libnpx/-/libnpx-10.2.0.tgz", + "resolved": false, "integrity": "sha512-X28coei8/XRCt15cYStbLBph+KGhFra4VQhRBPuH/HHMkC5dxM8v24RVgUsvODKCrUZ0eTgiTqJp6zbl0sskQQ==", "requires": { "dotenv": "^5.0.1", @@ -11559,7 +11557,7 @@ }, "locate-path": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "resolved": false, "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "requires": { "p-locate": "^2.0.0", @@ -11568,7 +11566,7 @@ }, "lock-verify": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lock-verify/-/lock-verify-2.1.0.tgz", + "resolved": false, "integrity": "sha512-vcLpxnGvrqisKvLQ2C2v0/u7LVly17ak2YSgoK4PrdsYBXQIax19vhKiLfvKNFx7FRrpTnitrpzF/uuCMuorIg==", "requires": { "npm-package-arg": "^6.1.0", @@ -11577,7 +11575,7 @@ }, "lockfile": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.4.tgz", + "resolved": false, "integrity": "sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==", "requires": { "signal-exit": "^3.0.2" @@ -11585,12 +11583,12 @@ }, "lodash._baseindexof": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz", + "resolved": false, "integrity": "sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw=" }, "lodash._baseuniq": { "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz", + "resolved": false, "integrity": "sha1-DrtE5FaBSveQXGIS+iybLVG4Qeg=", "requires": { "lodash._createset": "~4.0.0", @@ -11599,17 +11597,17 @@ }, "lodash._bindcallback": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz", + "resolved": false, "integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4=" }, "lodash._cacheindexof": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz", + "resolved": false, "integrity": "sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI=" }, "lodash._createcache": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lodash._createcache/-/lodash._createcache-3.1.2.tgz", + "resolved": false, "integrity": "sha1-VtagZAF2JeeevKa4AY4XRAvc8JM=", "requires": { "lodash._getnative": "^3.0.0" @@ -11617,52 +11615,52 @@ }, "lodash._createset": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/lodash._createset/-/lodash._createset-4.0.3.tgz", + "resolved": false, "integrity": "sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY=" }, "lodash._getnative": { "version": "3.9.1", - "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "resolved": false, "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=" }, "lodash._root": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", + "resolved": false, "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=" }, "lodash.clonedeep": { "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "resolved": false, "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" }, "lodash.restparam": { "version": "3.6.1", - "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", + "resolved": false, "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=" }, "lodash.union": { "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "resolved": false, "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=" }, "lodash.uniq": { "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "resolved": false, "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" }, "lodash.without": { "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.without/-/lodash.without-4.4.0.tgz", + "resolved": false, "integrity": "sha1-PNRXSgC2e643OpS3SHcmQFB7eqw=" }, "lowercase-keys": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "resolved": false, "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" }, "lru-cache": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "resolved": false, "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "requires": { "yallist": "^3.0.2" @@ -11670,7 +11668,7 @@ }, "make-dir": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "resolved": false, "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", "requires": { "pify": "^3.0.0" @@ -11696,12 +11694,12 @@ }, "meant": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/meant/-/meant-1.0.1.tgz", + "resolved": false, "integrity": "sha512-UakVLFjKkbbUwNWJ2frVLnnAtbb7D7DsloxRd3s/gDpI8rdv8W5Hp3NaDb+POBI1fQdeussER6NB8vpcRURvlg==" }, "mem": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "resolved": false, "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", "requires": { "mimic-fn": "^1.0.0" @@ -11709,12 +11707,12 @@ }, "mime-db": { "version": "1.35.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz", + "resolved": false, "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==" }, "mime-types": { "version": "2.1.19", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz", + "resolved": false, "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", "requires": { "mime-db": "~1.35.0" @@ -11722,12 +11720,12 @@ }, "mimic-fn": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "resolved": false, "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" }, "minimatch": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "requires": { "brace-expansion": "^1.1.7" @@ -11735,12 +11733,12 @@ }, "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": false, "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "minipass": { "version": "2.3.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.3.tgz", + "resolved": false, "integrity": "sha512-/jAn9/tEX4gnpyRATxgHEOV6xbcyxgT7iUnxo9Y3+OB0zX00TgKIv/2FZCf5brBbICcwbLqVv2ImjvWWrQMSYw==", "requires": { "safe-buffer": "^5.1.2", @@ -11749,14 +11747,14 @@ "dependencies": { "yallist": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", + "resolved": false, "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=" } } }, "minizlib": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", + "resolved": false, "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", "requires": { "minipass": "^2.2.1" @@ -11764,7 +11762,7 @@ }, "mississippi": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "resolved": false, "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", "requires": { "concat-stream": "^1.5.0", @@ -11781,7 +11779,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": false, "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { "minimist": "0.0.8" @@ -11789,7 +11787,7 @@ }, "move-concurrently": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "resolved": false, "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", "requires": { "aproba": "^1.1.1", @@ -11802,24 +11800,24 @@ "dependencies": { "aproba": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "resolved": false, "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" } } }, "ms": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "resolved": false, "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" }, "mute-stream": { "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "resolved": false, "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" }, "node-fetch-npm": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz", + "resolved": false, "integrity": "sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw==", "requires": { "encoding": "^0.1.11", @@ -11847,7 +11845,7 @@ "dependencies": { "nopt": { "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "resolved": false, "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", "requires": { "abbrev": "1" @@ -11855,14 +11853,14 @@ }, "semver": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "resolved": false, "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" } } }, "nopt": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "resolved": false, "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "requires": { "abbrev": "1", @@ -11871,7 +11869,7 @@ }, "normalize-package-data": { "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "resolved": false, "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "requires": { "hosted-git-info": "^2.1.4", @@ -11882,7 +11880,7 @@ "dependencies": { "resolve": { "version": "1.10.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "resolved": false, "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", "requires": { "path-parse": "^1.0.6" @@ -11892,7 +11890,7 @@ }, "npm-audit-report": { "version": "1.3.2", - "resolved": "https://registry.npmjs.org/npm-audit-report/-/npm-audit-report-1.3.2.tgz", + "resolved": false, "integrity": "sha512-abeqS5ONyXNaZJPGAf6TOUMNdSe1Y6cpc9MLBRn+CuUoYbfdca6AxOyXVlfIv9OgKX+cacblbG5w7A6ccwoTPw==", "requires": { "cli-table3": "^0.5.0", @@ -11901,17 +11899,17 @@ }, "npm-bundled": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", + "resolved": false, "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==" }, "npm-cache-filename": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/npm-cache-filename/-/npm-cache-filename-1.0.2.tgz", + "resolved": false, "integrity": "sha1-3tMGxbC/yHCp6fr4I7xfKD4FrhE=" }, "npm-install-checks": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-3.0.0.tgz", + "resolved": false, "integrity": "sha1-1K7N/VGlPjcjt7L5Oy7ijjB7wNc=", "requires": { "semver": "^2.3.0 || 3.x || 4 || 5" @@ -11934,12 +11932,12 @@ }, "npm-logical-tree": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/npm-logical-tree/-/npm-logical-tree-1.2.1.tgz", + "resolved": false, "integrity": "sha512-AJI/qxDB2PWI4LG1CYN579AY1vCiNyWfkiquCsJWqntRu/WwimVrC8yXeILBFHDwxfOejxewlmnvW9XXjMlYIg==" }, "npm-package-arg": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.0.tgz", + "resolved": false, "integrity": "sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA==", "requires": { "hosted-git-info": "^2.6.0", @@ -11950,7 +11948,7 @@ }, "npm-packlist": { "version": "1.4.4", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.4.tgz", + "resolved": false, "integrity": "sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw==", "requires": { "ignore-walk": "^3.0.1", @@ -11959,7 +11957,7 @@ }, "npm-pick-manifest": { "version": "2.2.3", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-2.2.3.tgz", + "resolved": false, "integrity": "sha512-+IluBC5K201+gRU85vFlUwX3PFShZAbAgDNp2ewJdWMVSppdo/Zih0ul2Ecky/X7b51J7LrrUAP+XOmOCvYZqA==", "requires": { "figgy-pudding": "^3.5.1", @@ -11992,7 +11990,7 @@ }, "npm-run-path": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "resolved": false, "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "requires": { "path-key": "^2.0.0" @@ -12000,12 +11998,12 @@ }, "npm-user-validate": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/npm-user-validate/-/npm-user-validate-1.0.0.tgz", + "resolved": false, "integrity": "sha1-jOyg9c6gTU6TUZ73LQVXp1Ei6VE=" }, "npmlog": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "resolved": false, "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "requires": { "are-we-there-yet": "~1.1.2", @@ -12016,27 +12014,27 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "resolved": false, "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, "oauth-sign": { "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "resolved": false, "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, "object-assign": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "resolved": false, "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-keys": { "version": "1.0.12", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", + "resolved": false, "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==" }, "object.getownpropertydescriptors": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "resolved": false, "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", "requires": { "define-properties": "^1.1.2", @@ -12045,7 +12043,7 @@ }, "once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "requires": { "wrappy": "1" @@ -12053,17 +12051,17 @@ }, "opener": { "version": "1.5.1", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz", + "resolved": false, "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==" }, "os-homedir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "resolved": false, "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" }, "os-locale": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "resolved": false, "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", "requires": { "execa": "^0.7.0", @@ -12073,12 +12071,12 @@ }, "os-tmpdir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": false, "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, "osenv": { "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "resolved": false, "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "requires": { "os-homedir": "^1.0.0", @@ -12087,12 +12085,12 @@ }, "p-finally": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "resolved": false, "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" }, "p-limit": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", + "resolved": false, "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", "requires": { "p-try": "^1.0.0" @@ -12100,7 +12098,7 @@ }, "p-locate": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "resolved": false, "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "requires": { "p-limit": "^1.1.0" @@ -12108,12 +12106,12 @@ }, "p-try": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "resolved": false, "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" }, "package-json": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", + "resolved": false, "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", "requires": { "got": "^6.7.1", @@ -12158,7 +12156,7 @@ "dependencies": { "minipass": { "version": "2.3.5", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", + "resolved": false, "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "requires": { "safe-buffer": "^5.1.2", @@ -12169,7 +12167,7 @@ }, "parallel-transform": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", + "resolved": false, "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", "requires": { "cyclist": "~0.2.2", @@ -12179,7 +12177,7 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": false, "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -12193,7 +12191,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -12203,57 +12201,57 @@ }, "path-exists": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "resolved": false, "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": false, "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-is-inside": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "resolved": false, "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" }, "path-key": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "resolved": false, "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" }, "path-parse": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "resolved": false, "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" }, "performance-now": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "resolved": false, "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "pify": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "resolved": false, "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" }, "prepend-http": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "resolved": false, "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" }, "process-nextick-args": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "resolved": false, "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" }, "promise-inflight": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "resolved": false, "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=" }, "promise-retry": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz", + "resolved": false, "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=", "requires": { "err-code": "^1.0.0", @@ -12262,14 +12260,14 @@ "dependencies": { "retry": { "version": "0.10.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", + "resolved": false, "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=" } } }, "promzard": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/promzard/-/promzard-0.3.0.tgz", + "resolved": false, "integrity": "sha1-JqXW7ox97kyxIggwWs+5O6OCqe4=", "requires": { "read": "1" @@ -12277,12 +12275,12 @@ }, "proto-list": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "resolved": false, "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=" }, "protoduck": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.1.tgz", + "resolved": false, "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==", "requires": { "genfun": "^5.0.0" @@ -12290,22 +12288,22 @@ }, "prr": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "resolved": false, "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" }, "pseudomap": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "resolved": false, "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, "psl": { "version": "1.1.29", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", + "resolved": false, "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==" }, "pump": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "resolved": false, "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "requires": { "end-of-stream": "^1.1.0", @@ -12314,7 +12312,7 @@ }, "pumpify": { "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "resolved": false, "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", "requires": { "duplexify": "^3.6.0", @@ -12324,7 +12322,7 @@ "dependencies": { "pump": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "resolved": false, "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", "requires": { "end-of-stream": "^1.1.0", @@ -12335,17 +12333,17 @@ }, "punycode": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "resolved": false, "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" }, "qrcode-terminal": { "version": "0.12.0", - "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz", + "resolved": false, "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==" }, "qs": { "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "resolved": false, "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, "query-string": { @@ -12360,12 +12358,12 @@ }, "qw": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/qw/-/qw-1.0.1.tgz", + "resolved": false, "integrity": "sha1-77/cdA+a0FQwRCassYNBLMi5ltQ=" }, "rc": { "version": "1.2.7", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.7.tgz", + "resolved": false, "integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==", "requires": { "deep-extend": "^0.5.1", @@ -12376,14 +12374,14 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": false, "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" } } }, "read": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "resolved": false, "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", "requires": { "mute-stream": "~0.0.4" @@ -12391,7 +12389,7 @@ }, "read-cmd-shim": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-1.0.1.tgz", + "resolved": false, "integrity": "sha1-LV0Vd4ajfAVdIgd8MsU/gynpHHs=", "requires": { "graceful-fs": "^4.1.2" @@ -12399,7 +12397,7 @@ }, "read-installed": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/read-installed/-/read-installed-4.0.3.tgz", + "resolved": false, "integrity": "sha1-/5uLZ/GH0eTCm5/rMfayI6zRkGc=", "requires": { "debuglog": "^1.0.1", @@ -12413,7 +12411,7 @@ }, "read-package-json": { "version": "2.0.13", - "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.0.13.tgz", + "resolved": false, "integrity": "sha512-/1dZ7TRZvGrYqE0UAfN6qQb5GYBsNcqS1C0tNK601CFOJmtHI7NIGXwetEPU/OtoFHZL3hDxm4rolFFVE9Bnmg==", "requires": { "glob": "^7.1.1", @@ -12425,7 +12423,7 @@ }, "read-package-tree": { "version": "5.3.1", - "resolved": "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.3.1.tgz", + "resolved": false, "integrity": "sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==", "requires": { "read-package-json": "^2.0.0", @@ -12435,7 +12433,7 @@ }, "readable-stream": { "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "resolved": false, "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", "requires": { "inherits": "^2.0.3", @@ -12445,7 +12443,7 @@ }, "readdir-scoped-modules": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", + "resolved": false, "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", "requires": { "debuglog": "^1.0.1", @@ -12456,7 +12454,7 @@ }, "registry-auth-token": { "version": "3.3.2", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", + "resolved": false, "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", "requires": { "rc": "^1.1.6", @@ -12465,7 +12463,7 @@ }, "registry-url": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "resolved": false, "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", "requires": { "rc": "^1.0.1" @@ -12473,7 +12471,7 @@ }, "request": { "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "resolved": false, "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", "requires": { "aws-sign2": "~0.7.0", @@ -12500,27 +12498,27 @@ }, "require-directory": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "resolved": false, "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" }, "require-main-filename": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "resolved": false, "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" }, "resolve-from": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "resolved": false, "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" }, "retry": { "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "resolved": false, "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" }, "rimraf": { "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "resolved": false, "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "requires": { "glob": "^7.1.3" @@ -12528,7 +12526,7 @@ }, "run-queue": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "resolved": false, "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", "requires": { "aproba": "^1.1.1" @@ -12536,29 +12534,29 @@ "dependencies": { "aproba": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "resolved": false, "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" } } }, "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "resolved": false, "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "semver": { "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "resolved": false, "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" }, "semver-diff": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", + "resolved": false, "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", "requires": { "semver": "^5.0.3" @@ -12566,12 +12564,12 @@ }, "set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "resolved": false, "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, "sha": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/sha/-/sha-3.0.0.tgz", + "resolved": false, "integrity": "sha512-DOYnM37cNsLNSGIG/zZWch5CKIRNoLdYUQTQlcgkRkoYIUwDYjqDyye16YcDZg/OPdcbUgTKMjc4SY6TB7ZAPw==", "requires": { "graceful-fs": "^4.1.2" @@ -12579,7 +12577,7 @@ }, "shebang-command": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "resolved": false, "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "requires": { "shebang-regex": "^1.0.0" @@ -12587,22 +12585,22 @@ }, "shebang-regex": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "resolved": false, "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" }, "signal-exit": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "resolved": false, "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, "slash": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "resolved": false, "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=" }, "slide": { "version": "1.1.6", - "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "resolved": false, "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=" }, "smart-buffer": { @@ -12640,12 +12638,12 @@ }, "sorted-object": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/sorted-object/-/sorted-object-2.0.1.tgz", + "resolved": false, "integrity": "sha1-fWMfS9OnmKJK8d/8+/6DM3pd9fw=" }, "sorted-union-stream": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/sorted-union-stream/-/sorted-union-stream-2.1.3.tgz", + "resolved": false, "integrity": "sha1-x3lMfgd4gAUv9xqNSi27Sppjisc=", "requires": { "from2": "^1.3.0", @@ -12654,7 +12652,7 @@ "dependencies": { "from2": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-1.3.0.tgz", + "resolved": false, "integrity": "sha1-iEE7qqX5pZfP3pIh2GmGzTwGHf0=", "requires": { "inherits": "~2.0.1", @@ -12663,12 +12661,12 @@ }, "isarray": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "resolved": false, "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" }, "readable-stream": { "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "resolved": false, "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { "core-util-is": "~1.0.0", @@ -12679,14 +12677,14 @@ }, "string_decoder": { "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "resolved": false, "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" } } }, "spdx-correct": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", + "resolved": false, "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", "requires": { "spdx-expression-parse": "^3.0.0", @@ -12695,12 +12693,12 @@ }, "spdx-exceptions": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", + "resolved": false, "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==" }, "spdx-expression-parse": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "resolved": false, "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", "requires": { "spdx-exceptions": "^2.1.0", @@ -12709,17 +12707,17 @@ }, "spdx-license-ids": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz", + "resolved": false, "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==" }, "split-on-first": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "resolved": false, "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==" }, "sshpk": { "version": "1.14.2", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", + "resolved": false, "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", "requires": { "asn1": "~0.2.3", @@ -12735,7 +12733,7 @@ }, "ssri": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "resolved": false, "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", "requires": { "figgy-pudding": "^3.5.1" @@ -12743,7 +12741,7 @@ }, "stream-each": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.2.tgz", + "resolved": false, "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==", "requires": { "end-of-stream": "^1.1.0", @@ -12752,7 +12750,7 @@ }, "stream-iterate": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/stream-iterate/-/stream-iterate-1.2.0.tgz", + "resolved": false, "integrity": "sha1-K9fHcpbBcCpGSIuK1B95hl7s1OE=", "requires": { "readable-stream": "^2.1.5", @@ -12761,7 +12759,7 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": false, "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -12775,7 +12773,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -12785,17 +12783,17 @@ }, "stream-shift": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "resolved": false, "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=" }, "strict-uri-encode": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "resolved": false, "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=" }, "string-width": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "resolved": false, "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "requires": { "is-fullwidth-code-point": "^2.0.0", @@ -12804,17 +12802,17 @@ "dependencies": { "ansi-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "resolved": false, "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" }, "is-fullwidth-code-point": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "resolved": false, "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, "strip-ansi": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "resolved": false, "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "requires": { "ansi-regex": "^3.0.0" @@ -12824,7 +12822,7 @@ }, "string_decoder": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", + "resolved": false, "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", "requires": { "safe-buffer": "~5.1.0" @@ -12832,12 +12830,12 @@ }, "stringify-package": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.0.tgz", + "resolved": false, "integrity": "sha512-JIQqiWmLiEozOC0b0BtxZ/AOUtdUZHCBPgqIZ2kSJJqGwgb9neo44XdTHUC4HZSGqi03hOeB7W/E8rAlKnGe9g==" }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": false, "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -12845,17 +12843,17 @@ }, "strip-eof": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "resolved": false, "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" }, "strip-json-comments": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "resolved": false, "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" }, "supports-color": { "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "resolved": false, "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "requires": { "has-flag": "^3.0.0" @@ -12863,7 +12861,7 @@ }, "tar": { "version": "4.4.10", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.10.tgz", + "resolved": false, "integrity": "sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA==", "requires": { "chownr": "^1.1.1", @@ -12877,7 +12875,7 @@ "dependencies": { "minipass": { "version": "2.3.5", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", + "resolved": false, "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "requires": { "safe-buffer": "^5.1.2", @@ -12886,14 +12884,14 @@ }, "yallist": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "resolved": false, "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" } } }, "term-size": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", + "resolved": false, "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", "requires": { "execa": "^0.7.0" @@ -12901,17 +12899,17 @@ }, "text-table": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "resolved": false, "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" }, "through": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": false, "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, "through2": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "resolved": false, "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", "requires": { "readable-stream": "^2.1.5", @@ -12920,7 +12918,7 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": false, "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -12934,7 +12932,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -12944,17 +12942,17 @@ }, "timed-out": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "resolved": false, "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" }, "tiny-relative-date": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/tiny-relative-date/-/tiny-relative-date-1.3.0.tgz", + "resolved": false, "integrity": "sha512-MOQHpzllWxDCHHaDno30hhLfbouoYlOI8YlMNtvKe1zXbjEVhbcEovQxvZrPvtiYW630GQDoMMarCnjfyfHA+A==" }, "tough-cookie": { "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "resolved": false, "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "requires": { "psl": "^1.1.24", @@ -12963,7 +12961,7 @@ }, "tunnel-agent": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "resolved": false, "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "requires": { "safe-buffer": "^5.0.1" @@ -12971,28 +12969,28 @@ }, "tweetnacl": { "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "resolved": false, "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "optional": true }, "typedarray": { "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "resolved": false, "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, "uid-number": { "version": "0.0.6", - "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", + "resolved": false, "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=" }, "umask": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/umask/-/umask-1.1.0.tgz", + "resolved": false, "integrity": "sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0=" }, "unique-filename": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "resolved": false, "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", "requires": { "unique-slug": "^2.0.0" @@ -13000,7 +12998,7 @@ }, "unique-slug": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz", + "resolved": false, "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=", "requires": { "imurmurhash": "^0.1.4" @@ -13008,7 +13006,7 @@ }, "unique-string": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", + "resolved": false, "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", "requires": { "crypto-random-string": "^1.0.0" @@ -13016,17 +13014,17 @@ }, "unpipe": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "resolved": false, "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, "unzip-response": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", + "resolved": false, "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=" }, "update-notifier": { "version": "2.5.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", + "resolved": false, "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", "requires": { "boxen": "^1.2.1", @@ -13043,7 +13041,7 @@ }, "url-parse-lax": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "resolved": false, "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", "requires": { "prepend-http": "^1.0.1" @@ -13051,17 +13049,17 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "resolved": false, "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "util-extend": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz", + "resolved": false, "integrity": "sha1-p8IW0mdUUWljeztu3GypEZ4v+T8=" }, "util-promisify": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/util-promisify/-/util-promisify-2.1.0.tgz", + "resolved": false, "integrity": "sha1-PCI2R2xNMsX/PEcAKt18E7moKlM=", "requires": { "object.getownpropertydescriptors": "^2.0.3" @@ -13069,12 +13067,12 @@ }, "uuid": { "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "resolved": false, "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" }, "validate-npm-package-license": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "resolved": false, "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "requires": { "spdx-correct": "^3.0.0", @@ -13083,7 +13081,7 @@ }, "validate-npm-package-name": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "resolved": false, "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", "requires": { "builtins": "^1.0.3" @@ -13091,7 +13089,7 @@ }, "verror": { "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "resolved": false, "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "requires": { "assert-plus": "^1.0.0", @@ -13101,7 +13099,7 @@ }, "wcwidth": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "resolved": false, "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", "requires": { "defaults": "^1.0.3" @@ -13109,7 +13107,7 @@ }, "which": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "resolved": false, "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "requires": { "isexe": "^2.0.0" @@ -13117,12 +13115,12 @@ }, "which-module": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "resolved": false, "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" }, "wide-align": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", + "resolved": false, "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", "requires": { "string-width": "^1.0.2" @@ -13130,7 +13128,7 @@ "dependencies": { "string-width": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { "code-point-at": "^1.0.0", @@ -13142,7 +13140,7 @@ }, "widest-line": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.0.tgz", + "resolved": false, "integrity": "sha1-AUKk6KJD+IgsAjOqDgKBqnYVInM=", "requires": { "string-width": "^2.1.1" @@ -13150,7 +13148,7 @@ }, "worker-farm": { "version": "1.7.0", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "resolved": false, "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", "requires": { "errno": "~0.1.7" @@ -13158,7 +13156,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": false, "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "requires": { "string-width": "^1.0.1", @@ -13167,7 +13165,7 @@ "dependencies": { "string-width": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { "code-point-at": "^1.0.0", @@ -13179,12 +13177,12 @@ }, "wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "resolved": false, "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write-file-atomic": { "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "resolved": false, "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", "requires": { "graceful-fs": "^4.1.11", @@ -13194,27 +13192,27 @@ }, "xdg-basedir": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", + "resolved": false, "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=" }, "xtend": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "resolved": false, "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" }, "y18n": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "resolved": false, "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" }, "yallist": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "resolved": false, "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" }, "yargs": { "version": "11.0.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.0.0.tgz", + "resolved": false, "integrity": "sha512-Rjp+lMYQOWtgqojx1dEWorjCofi1YN7AoFvYV7b1gx/7dAAeuI4kN5SZiEvr0ZmsZTOpDRcCqrpI10L31tFkBw==", "requires": { "cliui": "^4.0.0", @@ -13233,14 +13231,14 @@ "dependencies": { "y18n": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "resolved": false, "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" } } }, "yargs-parser": { "version": "9.0.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", + "resolved": false, "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", "requires": { "camelcase": "^4.1.0" diff --git a/package.json b/package.json index 2994bd8f8..31fbbcbe5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "uni2work", - "version": "5.1.0", + "version": "5.5.0", "description": "", "keywords": [], "author": "", diff --git a/package.yaml b/package.yaml index 1d62936ac..cdeca6c8f 100644 --- a/package.yaml +++ b/package.yaml @@ -1,5 +1,5 @@ name: uniworx -version: 5.1.0 +version: 5.5.0 dependencies: # Due to a bug in GHC 8.0.1, we block its usage @@ -194,7 +194,7 @@ ghc-options: - -fno-warn-unrecognised-pragmas - -fno-warn-partial-type-signatures - -fno-max-relevant-binds - - -j3 + - -j when: - condition: flag(pedantic) diff --git a/routes b/routes index cec9b58f7..b8c14a9e7 100644 --- a/routes +++ b/routes @@ -61,6 +61,7 @@ /info InfoR GET !free /info/lecturer InfoLecturerR GET !lecturer /info/data DataProtR GET !free +/info/allocation InfoAllocationR GET !free /impressum ImpressumR GET !free /version VersionR GET !free @@ -80,11 +81,11 @@ /school SchoolListR GET !development /school/#SchoolId SchoolShowR GET !development +/allocation/ AllocationListR GET !free /allocation/#TermId/#SchoolId/#AllocationShorthand AllocationR: / AShowR GET !free /register ARegisterR POST !time /course/#CryptoUUIDCourse/apply AApplyR POST !timeANDallocation-registered - /application/#CryptoFileNameCourseApplication AApplicationR GET POST !timeANDself !lecturerANDstaff-time !selfANDread -- For Pattern Synonyms see Foundation @@ -106,11 +107,11 @@ /notes CNotesR GET POST !corrector -- THIS route is used to check for overall course corrector access! /subs CCorrectionsR GET POST /subs/assigned CAssignR GET POST - /ex SheetListR GET !course-registered !materials !corrector - /ex/new SheetNewR GET POST - /ex/current SheetCurrentR GET !course-registered !materials !corrector - /ex/unassigned SheetOldUnassignedR GET - /ex/#SheetName SheetR: + /sheet SheetListR GET !course-registered !materials !corrector + /sheet/new SheetNewR GET POST + /sheet/current SheetCurrentR GET !course-registered !materials !corrector + /sheet/unassigned SheetOldUnassignedR GET + /sheet/#SheetName SheetR: /show SShowR GET !timeANDcourse-registered !timeANDmaterials !corrector !timeANDtutor /show/download SArchiveR GET !timeANDcourse-registered !timeANDmaterials !corrector !timeANDtutor /edit SEditR GET POST @@ -161,8 +162,10 @@ /users/invite EInviteR GET POST /register ERegisterR POST !timeANDcourse-registeredAND¬exam-registered !timeANDexam-registeredAND¬exam-result /apps CApplicationsR GET POST + !/apps/files CAppsFilesR GET /apps/#CryptoFileNameCourseApplication CourseApplicationR: - /files CAFilesR GET !self !lecturerANDtime + / CAEditR GET POST !timeANDself !lecturerANDstaff-time !selfANDread + /files CAFilesR GET !self !lecturerANDstaff-time /subs CorrectionsR GET POST !corrector !lecturer /subs/upload CorrectionsUploadR GET POST !corrector !lecturer diff --git a/src/Application.hs b/src/Application.hs index 597a316fd..fe1bc98ff 100644 --- a/src/Application.hs +++ b/src/Application.hs @@ -64,10 +64,6 @@ import qualified Yesod.Core.Types as Yesod (Logger(..)) import qualified Data.HashMap.Strict as HashMap -import Utils.Lens - -import Data.Proxy - import qualified Data.Aeson as Aeson import System.Exit diff --git a/src/Auth/LDAP.hs b/src/Auth/LDAP.hs index 26026dfee..406a3a2d4 100644 --- a/src/Auth/LDAP.hs +++ b/src/Auth/LDAP.hs @@ -10,7 +10,6 @@ module Auth.LDAP ) where import Import.NoFoundation hiding (userEmail, userDisplayName) -import Control.Lens import Network.Connection import Data.CaseInsensitive (CI) diff --git a/src/Colonnade/Instances.hs b/src/Colonnade/Instances.hs new file mode 100644 index 000000000..d47902515 --- /dev/null +++ b/src/Colonnade/Instances.hs @@ -0,0 +1,19 @@ +{-# OPTIONS_GHC -fno-warn-orphans #-} + +module Colonnade.Instances + ( + ) where + +import ClassyPrelude + +import Control.Lens.Indexed (FunctorWithIndex(imap)) + +import Colonnade.Encode (Colonnade(..), OneColonnade(..)) + +instance Functor h => FunctorWithIndex (Maybe a) (Colonnade h a) where + imap f (Colonnade ones) = Colonnade $ dimapColonnade' <$> ones + where + dimapColonnade' OneColonnade{..} = OneColonnade + { oneColonnadeEncode = \x -> f (Just x) $ oneColonnadeEncode x + , oneColonnadeHead = f Nothing <$> oneColonnadeHead + } diff --git a/src/Data/Bool/Instances.hs b/src/Data/Bool/Instances.hs new file mode 100644 index 000000000..d5eb7a2e0 --- /dev/null +++ b/src/Data/Bool/Instances.hs @@ -0,0 +1,28 @@ +{-# OPTIONS_GHC -fno-warn-orphans #-} + +module Data.Bool.Instances + () where + +import ClassyPrelude + +import qualified Data.Csv as Csv +import Data.CaseInsensitive (CI) +import qualified Data.CaseInsensitive as CI +import Data.CaseInsensitive.Instances () + +import qualified Data.Text as Text + + +instance Csv.ToField Bool where + toField True = "t" + toField False = "f" + +instance Csv.FromField Bool where + parseField f = do + (CI.map Text.strip -> t :: CI Text) <- Csv.parseField f + (True <$ guard (isTrue t)) <|> (False <$ guard (isFalse t)) <|> fail "Could not decode Bool" + where + isTrue = flip elem + [ "yes", "y", "ja", "j", "wahr", "w", "true", "t", "1" ] + isFalse = flip elem + [ "no", "n", "nein", "falsch", "f", "false", "0" ] diff --git a/src/Data/CryptoID/Instances.hs b/src/Data/CryptoID/Instances.hs index bc66cb874..0867f60b5 100644 --- a/src/Data/CryptoID/Instances.hs +++ b/src/Data/CryptoID/Instances.hs @@ -16,6 +16,8 @@ import qualified Data.CaseInsensitive as CI import Web.PathPieces import Data.Aeson (ToJSON(..), ToJSONKey(..), ToJSONKeyFunction(..)) +import qualified Data.Csv as Csv + instance ToMarkup s => ToMarkup (CID.CryptoID c s) where toMarkup = toMarkup . CID.ciphertext @@ -34,3 +36,12 @@ instance {-# OVERLAPS #-} (ToJSON s, ToJSONKey s) => ToJSONKey (CID.CryptoID c ( instance {-# OVERLAPS #-} (PathPiece s, CI.FoldCase s) => PathPiece (CID.CryptoID c (CI s)) where toPathPiece = toPathPiece . CI.foldedCase . CID.ciphertext fromPathPiece = fmap (CID.CryptoID . CI.mk) . fromPathPiece + +instance Csv.FromField s => Csv.FromField (CID.CryptoID c s) where + parseField = fmap CID.CryptoID . Csv.parseField + +instance Csv.ToField s => Csv.ToField (CID.CryptoID c s) where + toField = Csv.toField . CID.ciphertext + +instance {-# OVERLAPS #-} (Csv.ToField s, CI.FoldCase s) => Csv.ToField (CID.CryptoID c (CI s)) where + toField = Csv.toField . CI.foldedCase . CID.ciphertext diff --git a/src/Data/Time/Clock/Instances.hs b/src/Data/Time/Clock/Instances.hs index b9721ab7d..9629800d1 100644 --- a/src/Data/Time/Clock/Instances.hs +++ b/src/Data/Time/Clock/Instances.hs @@ -6,17 +6,27 @@ module Data.Time.Clock.Instances import ClassyPrelude -import Data.Time.Clock +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 () instance Hashable DiffTime where hashWithSalt s = hashWithSalt s . toRational +instance PersistField NominalDiffTime where + toPersistValue = toPersistValue . toRational + fromPersistValue = fmap fromRational . fromPersistValue + +instance PersistFieldSql NominalDiffTime where + sqlType _ = sqlType (Proxy @Rational) + deriving instance Generic UTCTime instance Hashable UTCTime @@ -25,5 +35,5 @@ instance Hashable UTCTime instance Binary DiffTime where get = fromRational <$> Binary.get put = Binary.put . toRational - + instance Binary UTCTime diff --git a/src/Database/Esqueleto/Utils.hs b/src/Database/Esqueleto/Utils.hs index 201091a2d..74bfbb7d6 100644 --- a/src/Database/Esqueleto/Utils.hs +++ b/src/Database/Esqueleto/Utils.hs @@ -18,6 +18,7 @@ module Database.Esqueleto.Utils , SqlHashable , sha256 , maybe + , SqlProject(..) ) where @@ -161,21 +162,17 @@ mkExistsFilter query row criterias | otherwise = any (E.exists . query row) $ Set.toList criterias -- | Combine several filters, using logical or -anyFilter :: (Foldable f) - => f (t -> Set.Set Text-> E.SqlExpr (E.Value Bool)) - -> t - -> Set.Set Text - -> E.SqlExpr (E.Value Bool) +anyFilter :: Foldable f + => f (t -> cs -> E.SqlExpr (E.Value Bool)) + -> (t -> cs -> E.SqlExpr (E.Value Bool)) anyFilter fltrs needle criterias = F.foldr aux false fltrs where aux fltr acc = fltr needle criterias E.||. acc -- | Combine several filters, using logical and -allFilter :: (Foldable f) - => f (t -> Set.Set Text-> E.SqlExpr (E.Value Bool)) - -> t - -> Set.Set Text - -> E.SqlExpr (E.Value Bool) +allFilter :: Foldable f + => f (t -> cs -> E.SqlExpr (E.Value Bool)) + -> (t -> cs -> E.SqlExpr (E.Value Bool)) allFilter fltrs needle criterias = F.foldr aux true fltrs where aux fltr acc = fltr needle criterias E.&&. acc @@ -232,3 +229,16 @@ maybe onNothing onJust val = E.case_ (onJust $ E.veryUnsafeCoerceSqlExprValue val) ] (E.else_ onNothing) + + +class (PersistEntity entity, PersistField value) => SqlProject entity value entity' value' | entity value entity' -> value', entity value value' -> entity' where + sqlProject :: E.SqlExpr entity' -> EntityField entity value -> E.SqlExpr (E.Value value') + unSqlProject :: forall p1 p2. p1 entity -> p2 entity' -> value -> value' + +instance (PersistEntity val, PersistField typ) => SqlProject val typ (E.Entity val) typ where + sqlProject = (E.^.) + unSqlProject _ _ = id + +instance (PersistEntity val, PersistField typ) => SqlProject val typ (Maybe (E.Entity val)) (Maybe typ) where + sqlProject = (E.?.) + unSqlProject _ _ = Just diff --git a/src/Foundation.hs b/src/Foundation.hs index 9efb01b35..8698da41c 100644 --- a/src/Foundation.hs +++ b/src/Foundation.hs @@ -65,7 +65,6 @@ import Control.Monad.Memo.Class (MonadMemo(..), for4) import qualified Control.Monad.Catch as C import Handler.Utils.StudyFeatures -import Utils.Lens import Utils.Form import Utils.Sheet import Utils.SystemMessage @@ -369,6 +368,11 @@ instance RenderMessage UniWorX a => RenderMessage UniWorX (ExamResult' a) where mr :: RenderMessage UniWorX msg => msg -> Text mr = renderMessage foundation ls +instance RenderMessage UniWorX (Either ExamPassed ExamGrade) where + renderMessage foundation ls = either mr mr + where + mr :: RenderMessage UniWorX msg => msg -> Text + mr = renderMessage foundation ls -- ToMessage instances for converting raw numbers to Text (no internationalization) @@ -661,22 +665,6 @@ tagAccessPredicate AuthLecturer = APDB $ \mAuthId route _ -> case route of E.&&. course E.^. CourseShorthand E.==. E.val csh guardMExceptT isLecturer (unauthorizedI MsgUnauthorizedLecturer) return Authorized - AllocationR tid ssh ash (AApplicationR cID) -> $cachedHereBinary (mAuthId, tid, ssh, ash, cID) . exceptT return return $ do - authId <- maybeExceptT AuthenticationRequired $ return mAuthId - appId <- catchIfMExceptT (const $ unauthorizedI MsgUnauthorizedLecturer) (const True :: CryptoIDError -> Bool) $ decrypt cID - isLecturer <- lift . E.selectExists . E.from $ \(courseApplication `E.InnerJoin` allocation `E.InnerJoin` allocationCourse `E.InnerJoin` course `E.InnerJoin` lecturer) -> do - E.on $ course E.^. CourseId E.==. lecturer E.^. LecturerCourse - E.on $ course E.^. CourseId E.==. allocationCourse E.^. AllocationCourseCourse - E.on $ allocation E.^. AllocationId E.==. allocationCourse E.^. AllocationCourseAllocation - E.on $ courseApplication E.^. CourseApplicationAllocation E.==. E.just (allocation E.^. AllocationId) - E.&&. courseApplication E.^. CourseApplicationCourse E.==. course E.^. CourseId - E.where_ $ lecturer E.^. LecturerUser E.==. E.val authId - E.&&. allocation E.^. AllocationTerm E.==. E.val tid - E.&&. allocation E.^. AllocationSchool E.==. E.val ssh - E.&&. allocation E.^. AllocationShorthand E.==. E.val ash - E.&&. courseApplication E.^. CourseApplicationId E.==. E.val appId - guardMExceptT isLecturer (unauthorizedI MsgUnauthorizedLecturer) - return Authorized AllocationR tid ssh ash _ -> $cachedHereBinary (mAuthId, tid, ssh, ash) . exceptT return return $ do authId <- maybeExceptT AuthenticationRequired $ return mAuthId isLecturer <- lift . E.selectExists . E.from $ \(allocation `E.InnerJoin` allocationCourse `E.InnerJoin` course `E.InnerJoin` lecturer) -> do @@ -868,11 +856,28 @@ tagAccessPredicate AuthTime = APDB $ \mAuthId route _ -> case route of guard $ NTop (Just now) >= NTop deregUntil return Authorized _other -> unauthorizedI MsgUnauthorizedCourseTime + + CApplicationR tid ssh csh _ _ -> maybeT (unauthorizedI MsgUnauthorizedApplicationTime) $ do + Entity course Course{..} <- $cachedHereBinary (tid, ssh, csh) . MaybeT . getBy $ TermSchoolCourseShort tid ssh csh + allocationCourse <- $cachedHereBinary course . lift . getBy $ UniqueAllocationCourse course + allocation <- for allocationCourse $ \(Entity _ AllocationCourse{..}) -> $cachedHereBinary allocationCourseAllocation . MaybeT $ get allocationCourseAllocation + + case allocation of + Nothing -> do + cTime <- liftIO getCurrentTime + guard $ maybe False (cTime >=) courseRegisterFrom + guard $ maybe True (cTime <=) courseRegisterTo + Just Allocation{..} -> do + cTime <- liftIO getCurrentTime + guard $ NTop allocationRegisterFrom <= NTop (Just cTime) + guard $ NTop (Just cTime) <= NTop allocationRegisterTo + + return Authorized AllocationR tid ssh ash _ -> maybeT (unauthorizedI MsgUnauthorizedAllocationRegisterTime) $ do -- Checks `registerFrom` and `registerTo`, override as further routes become available now <- liftIO getCurrentTime - Entity _ Allocation{..} <- MaybeT . $cachedHereBinary (tid, ssh, ash) . getBy $ TermSchoolAllocationShort tid ssh ash + Entity _ Allocation{..} <- MaybeT . $cachedHereBinary (tid, ssh, ash) . getBy $ TermSchoolAllocationShort tid ssh ash guard $ NTop allocationRegisterFrom <= NTop (Just now) guard $ NTop (Just now) <= NTop allocationRegisterTo return Authorized @@ -887,10 +892,24 @@ tagAccessPredicate AuthTime = APDB $ \mAuthId route _ -> case route of r -> $unsupportedAuthPredicate AuthTime r tagAccessPredicate AuthStaffTime = APDB $ \_ route _ -> case route of + CApplicationR tid ssh csh _ _ -> maybeT (unauthorizedI MsgUnauthorizedApplicationTime) $ do + course <- $cachedHereBinary (tid, ssh, csh) . MaybeT . getKeyBy $ TermSchoolCourseShort tid ssh csh + allocationCourse <- $cachedHereBinary course . lift . getBy $ UniqueAllocationCourse course + allocation <- for allocationCourse $ \(Entity _ AllocationCourse{..}) -> $cachedHereBinary allocationCourseAllocation . MaybeT $ get allocationCourseAllocation + + case allocation of + Nothing -> return () + Just Allocation{..} -> do + cTime <- liftIO getCurrentTime + guard $ NTop allocationStaffAllocationFrom <= NTop (Just cTime) + guard $ NTop (Just cTime) <= NTop allocationStaffAllocationTo + + return Authorized + AllocationR tid ssh ash _ -> maybeT (unauthorizedI MsgUnauthorizedAllocationRegisterTime) $ do -- Checks `registerFrom` and `registerTo`, override as further routes become available now <- liftIO getCurrentTime - Entity _ Allocation{..} <- MaybeT . $cachedHereBinary (tid, ssh, ash) . getBy $ TermSchoolAllocationShort tid ssh ash + Entity _ Allocation{..} <- MaybeT . $cachedHereBinary (tid, ssh, ash) . getBy $ TermSchoolAllocationShort tid ssh ash guard $ NTop allocationStaffAllocationFrom <= NTop (Just now) guard $ NTop (Just now) <= NTop allocationStaffAllocationTo return Authorized @@ -1042,6 +1061,7 @@ tagAccessPredicate AuthAllocationRegistered = APDB $ \mAuthId route _ -> case ro r -> $unsupportedAuthPredicate AuthAllocationRegistered r tagAccessPredicate AuthParticipant = APDB $ \_ route _ -> case route of CourseR tid ssh csh (CUserR cID) -> exceptT return return $ do + cTime <- liftIO getCurrentTime let authorizedIfExists f = do [E.Value ok] <- lift . E.select . return . E.exists $ E.from f whenExceptT ok Authorized @@ -1102,12 +1122,16 @@ tagAccessPredicate AuthParticipant = APDB $ \_ route _ -> case route of E.&&. course E.^. CourseSchool E.==. E.val ssh E.&&. course E.^. CourseShorthand E.==. E.val csh -- participant is applicant for this course - $cachedHereBinary (participant, tid, ssh, csh) . authorizedIfExists $ \(course `E.InnerJoin` courseApplication) -> do + $cachedHereBinary (participant, tid, ssh, csh) . authorizedIfExists $ \((course `E.InnerJoin` courseApplication) `E.LeftOuterJoin` allocation) -> do + E.on $ allocation E.?. AllocationId E.==. courseApplication E.^. CourseApplicationAllocation E.on $ course E.^. CourseId E.==. courseApplication E.^. CourseApplicationCourse E.where_ $ courseApplication E.^. CourseApplicationUser 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 + E.where_ $ E.maybe E.true (E.maybe E.false $ \f -> f E.<=. E.val cTime) (allocation E.?. AllocationStaffAllocationFrom) + E.&&. E.maybe E.true (E.maybe E.true $ \t -> t E.>=. E.val cTime) (allocation E.?. AllocationStaffAllocationTo) + unauthorizedI MsgUnauthorizedParticipant r -> $unsupportedAuthPredicate AuthParticipant r tagAccessPredicate AuthCapacity = APDB $ \_ route _ -> case route of @@ -1194,10 +1218,6 @@ tagAccessPredicate AuthSelf = APDB $ \mAuthId route _ -> exceptT return return $ appId <- catchIfMExceptT (const $ unauthorizedI MsgUnauthorizedSelf) (const True :: CryptoIDError -> Bool) $ decrypt cID CourseApplication{..} <- maybeMExceptT (unauthorizedI MsgUnauthorizedSelf) . $cachedHereBinary appId $ get appId return $ Right courseApplicationUser - AllocationR _ _ _ (AApplicationR cID) -> do - appId <- catchIfMExceptT (const $ unauthorizedI MsgUnauthorizedSelf) (const True :: CryptoIDError -> Bool) $ decrypt cID - CourseApplication{..} <- maybeMExceptT (unauthorizedI MsgUnauthorizedSelf) . $cachedHereBinary appId $ get appId - return $ Right courseApplicationUser _other -> throwError =<< $unsupportedAuthPredicate AuthSelf route referencedUser <- case referencedUser' of Right uid -> return uid @@ -1658,6 +1678,8 @@ siteLayout' headingOverride widget = do hasPageActions = hasPrimaryPageActions || hasSecondaryPageActions hasSecondaryPageActions = any (is _PageActionSecondary) $ toListOf (traverse . _1 . _menuItemType) menuTypes hasPrimaryPageActions = any (is _PageActionPrime) $ toListOf (traverse . _1 . _menuItemType) menuTypes + contentRibbon :: Maybe Widget + contentRibbon = fmap toWidget appRibbon MsgRenderer mr <- getMsgRenderer let @@ -1718,6 +1740,7 @@ instance YesodBreadcrumbs UniWorX where breadcrumb InfoR = return ("Information" , Nothing) breadcrumb InfoLecturerR = return ("Veranstalter" , Just InfoR) breadcrumb DataProtR = return ("Datenschutz" , Just InfoR) + breadcrumb InfoAllocationR = return ("Zentralanmeldungen", Just InfoR) breadcrumb ImpressumR = return ("Impressum" , Just InfoR) breadcrumb VersionR = return ("Versionsgeschichte", Just InfoR) @@ -1741,11 +1764,11 @@ instance YesodBreadcrumbs UniWorX where breadcrumb (TermSchoolCourseListR tid ssh) = return (original $ unSchoolKey ssh, Just $ TermCourseListR tid) + breadcrumb AllocationListR = return ("Zentralanmeldungen", Just HomeR) breadcrumb (AllocationR tid ssh ash AShowR) = do mr <- getMessageRender Entity _ Allocation{allocationName} <- runDB . getBy404 $ TermSchoolAllocationShort tid ssh ash - return ([st|#{allocationName} (#{mr (ShortTermIdentifier (unTermKey tid))}, #{original (unSchoolKey ssh)})|], Just $ HomeR) - breadcrumb (AllocationR tid ssh ash (AApplicationR _)) = return ("Bewerbung", Just $ AllocationR tid ssh ash AShowR) + return ([st|#{allocationName} (#{mr (ShortTermIdentifier (unTermKey tid))}, #{original (unSchoolKey ssh)})|], Just $ AllocationListR) breadcrumb CourseListR = return ("Kurse" , Nothing) breadcrumb CourseNewR = return ("Neu" , Just CourseListR) @@ -1769,6 +1792,10 @@ instance YesodBreadcrumbs UniWorX where breadcrumb (CourseR tid ssh csh CExamListR) = return ("Prüfungen", Just $ CourseR tid ssh csh CShowR) breadcrumb (CourseR tid ssh csh CExamNewR) = return ("Anlegen", Just $ CourseR tid ssh csh CExamListR) + breadcrumb (CourseR tid ssh csh CApplicationsR) = return ("Bewerbungen", Just $ CourseR tid ssh csh CShowR) + + breadcrumb (CApplicationR tid ssh csh _ CAEditR) = return ("Bewerbung", Just $ CourseR tid ssh csh CApplicationsR) + breadcrumb (CExamR tid ssh csh examn EShowR) = return (original examn, Just $ CourseR tid ssh csh CExamListR) breadcrumb (CExamR tid ssh csh examn EEditR) = return ("Bearbeiten", Just $ CExamR tid ssh csh examn EShowR) breadcrumb (CExamR tid ssh csh examn EUsersR) = return ("Teilnehmer", Just $ CExamR tid ssh csh examn EShowR) @@ -1963,35 +1990,19 @@ pageActions (HomeR) = , menuItemModal = False , menuItemAccessCallback' = return True } - , MenuItem + , MenuItem { menuItemType = PageActionPrime - , menuItemLabel = MsgAdminHeading - , menuItemIcon = Just "screwdriver" - , menuItemRoute = SomeRoute AdminR - , menuItemModal = False - , menuItemAccessCallback' = return True - } - , MenuItem - { menuItemType = PageActionPrime - , menuItemLabel = MsgAdminFeaturesHeading + , menuItemLabel = MsgMenuOpenCourses , menuItemIcon = Nothing - , menuItemRoute = SomeRoute AdminFeaturesR + , menuItemRoute = SomeRoute (CourseListR, [("courses-openregistration", "True")]) , menuItemModal = False , menuItemAccessCallback' = return True } , MenuItem { menuItemType = PageActionPrime - , menuItemLabel = MsgMenuMessageList + , menuItemLabel = MsgMenuOpenAllocations , menuItemIcon = Nothing - , menuItemRoute = SomeRoute MessageListR - , menuItemModal = False - , menuItemAccessCallback' = return True - } - , MenuItem - { menuItemType = PageActionPrime - , menuItemLabel = MsgMenuAdminErrMsg - , menuItemIcon = Nothing - , menuItemRoute = SomeRoute AdminErrMsgR + , menuItemRoute = SomeRoute (AllocationListR, [("allocations-active", "True")]) , menuItemModal = False , menuItemAccessCallback' = return True } @@ -2015,20 +2026,12 @@ pageActions (AdminR) = } , MenuItem { menuItemType = PageActionPrime - , menuItemLabel = MsgErrMsgHeading + , menuItemLabel = MsgMenuAdminErrMsg , menuItemIcon = Nothing , menuItemRoute = SomeRoute AdminErrMsgR , menuItemModal = False , menuItemAccessCallback' = return True } - , MenuItem - { menuItemType = PageActionPrime - , menuItemLabel = MsgMenuUsers - , menuItemIcon = Nothing - , menuItemRoute = SomeRoute UsersR - , menuItemModal = False - , menuItemAccessCallback' = return True - } , MenuItem { menuItemType = PageActionSecondary , menuItemLabel = MsgMenuAdminTest @@ -2167,7 +2170,7 @@ pageActions (TermCourseListR tid) = ] pageActions (TermSchoolCourseListR _tid _ssh) = [ MenuItem - { menuItemType = PageActionPrime + { menuItemType = PageActionPrime , menuItemLabel = MsgMenuCourseNew , menuItemIcon = Just "book" , menuItemRoute = SomeRoute CourseNewR @@ -2175,6 +2178,16 @@ pageActions (TermSchoolCourseListR _tid _ssh) = , menuItemAccessCallback' = return True } ] +pageActions (AllocationR _tid _ssh _ash AShowR) = + [ MenuItem + { menuItemType = PageActionPrime + , menuItemLabel = MsgMenuAllocationInfo + , menuItemIcon = Nothing + , menuItemRoute = SomeRoute InfoAllocationR + , menuItemModal = True + , menuItemAccessCallback' = return True + } + ] pageActions (CourseListR) = [ MenuItem { menuItemType = PageActionPrime @@ -2184,6 +2197,14 @@ pageActions (CourseListR) = , menuItemModal = False , menuItemAccessCallback' = return True } + , MenuItem + { menuItemType = PageActionPrime + , menuItemLabel = MsgMenuAllocationList + , menuItemIcon = Nothing + , menuItemRoute = SomeRoute AllocationListR + , menuItemModal = False + , menuItemAccessCallback' = return True + } ] pageActions (CourseNewR) = [ MenuItem @@ -2262,6 +2283,28 @@ pageActions (CourseR tid ssh csh CShowR) = anyM examNames $ examAccess . E.unValue in runDB $ lecturerAccess `or2M` existsVisible } + , MenuItem + { menuItemType = PageActionSecondary + , menuItemLabel = MsgMenuCourseApplications + , menuItemIcon = Nothing + , menuItemRoute = SomeRoute $ CourseR tid ssh csh CApplicationsR + , menuItemModal = False + , menuItemAccessCallback' = + let courseWhere course = course <$ do + E.where_ $ course E.^. CourseTerm E.==. E.val tid + E.&&. course E.^. CourseSchool E.==. E.val ssh + E.&&. course E.^. CourseShorthand E.==. E.val csh + existsApplications = E.selectExists . E.from $ \(course `E.InnerJoin` courseApplication) -> do + E.on $ course E.^. CourseId E.==. courseApplication E.^. CourseApplicationCourse + void $ courseWhere course + courseApplications = fmap (any E.unValue) . E.select . E.from $ \course -> do + void $ courseWhere course + return $ course E.^. CourseApplicationsRequired + courseAllocation = E.selectExists . E.from $ \(course `E.InnerJoin` allocationCourse) -> do + E.on $ course E.^. CourseId E.==. allocationCourse E.^. AllocationCourseCourse + void $ courseWhere course + in runDB $ courseAllocation `or2M` courseApplications `or2M` existsApplications + } , MenuItem { menuItemType = PageActionSecondary , menuItemLabel = MsgMenuCourseMembers @@ -2678,6 +2721,28 @@ pageActions (CSheetR tid ssh csh shn SCorrR) = , menuItemAccessCallback' = return True } ] +pageActions (CourseR tid ssh csh CApplicationsR) = + [ MenuItem + { menuItemType = PageActionPrime + , menuItemLabel = MsgMenuCourseApplicationsFiles + , menuItemIcon = Nothing + , menuItemRoute = SomeRoute $ CourseR tid ssh csh CAppsFilesR + , menuItemModal = False + , menuItemAccessCallback' + = let appAccess (E.Value appId) = do + cID <- encrypt appId + hasReadAccessTo $ CApplicationR tid ssh csh cID CAFilesR + appSource = E.selectSource . E.from $ \(course `E.InnerJoin` courseApplication) -> do + E.on $ course E.^. CourseId E.==. courseApplication E.^. CourseApplicationCourse + E.where_ $ course E.^. CourseTerm E.==. E.val tid + E.&&. course E.^. CourseSchool E.==. E.val ssh + E.&&. course E.^. CourseShorthand E.==. E.val csh + E.where_ . E.exists . E.from $ \courseApplicationFile -> + E.where_ $ courseApplicationFile E.^. CourseApplicationFileApplication E.==. courseApplication E.^. CourseApplicationId + return $ courseApplication E.^. CourseApplicationId + in runDB . runConduit $ appSource .| anyMC appAccess + } + ] pageActions (CorrectionsR) = [ MenuItem { menuItemType = PageActionPrime @@ -3028,6 +3093,7 @@ upsertCampusUser ldapData Creds{..} = do , userDateFormat = userDefaultDateFormat , userTimeFormat = userDefaultTimeFormat , userDownloadFiles = userDefaultDownloadFiles + , userWarningDays = userDefaultWarningDays , userNotificationSettings = def , userMailLanguages = def , userTokensIssuedAfter = Nothing diff --git a/src/Handler/Admin.hs b/src/Handler/Admin.hs index ded5ebec7..db6096bec 100644 --- a/src/Handler/Admin.hs +++ b/src/Handler/Admin.hs @@ -8,8 +8,6 @@ import Data.Aeson.Encode.Pretty (encodePrettyToTextBuilder) import Control.Monad.Trans.Except import Control.Monad.Trans.Writer (mapWriterT) -import Utils.Lens - -- import Data.Time import Data.Char (isDigit) import qualified Data.Text as Text diff --git a/src/Handler/Allocation.hs b/src/Handler/Allocation.hs index 286a87aa1..3cb35e7cc 100644 --- a/src/Handler/Allocation.hs +++ b/src/Handler/Allocation.hs @@ -2,6 +2,8 @@ module Handler.Allocation ( module Handler.Allocation ) where +import Handler.Allocation.Info as Handler.Allocation import Handler.Allocation.Show as Handler.Allocation import Handler.Allocation.Application as Handler.Allocation import Handler.Allocation.Register as Handler.Allocation +import Handler.Allocation.List as Handler.Allocation diff --git a/src/Handler/Allocation/Application.hs b/src/Handler/Allocation/Application.hs index cae507c55..bc19f3dc2 100644 --- a/src/Handler/Allocation/Application.hs +++ b/src/Handler/Allocation/Application.hs @@ -4,16 +4,13 @@ module Handler.Allocation.Application , ApplicationForm(..) , ApplicationFormMode(..) , ApplicationFormException(..) - , applicationForm + , applicationForm, editApplicationR , postAApplyR - , getAApplicationR, postAApplicationR ) where import Import hiding (hash) import Handler.Utils -import Utils.Lens - import qualified Data.Text as Text import qualified Data.Set as Set @@ -73,20 +70,21 @@ data ApplicationFormException = ApplicationFormNoApplication -- ^ Could not fill deriving (Eq, Ord, Read, Show, Generic, Typeable) instance Exception ApplicationFormException -applicationForm :: AllocationId +applicationForm :: (Maybe AllocationId) -> CourseId -> UserId -> ApplicationFormMode -- ^ Which parts of the shared form to display -> Html -> MForm Handler (FormResult ApplicationForm, ApplicationFormView) -applicationForm aId cid uid ApplicationFormMode{..} csrf = do +applicationForm maId@(is _Just -> isAlloc) cid uid ApplicationFormMode{..} csrf = do + (mApp, coursesNum, Course{..}, maxPrio) <- liftHandlerT . runDB $ do - mApplication <- listToMaybe <$> selectList [CourseApplicationAllocation ==. Just aId, CourseApplicationUser ==. uid, CourseApplicationCourse ==. cid] [LimitTo 1] - coursesNum <- fromIntegral <$> count [AllocationCourseAllocation ==. aId] + mApplication <- listToMaybe <$> selectList [CourseApplicationAllocation ==. maId, CourseApplicationUser ==. uid, CourseApplicationCourse ==. cid] [LimitTo 1] + coursesNum <- fromIntegral . fromMaybe 1 <$> for maId (\aId -> count [AllocationCourseAllocation ==. aId]) course <- getJust cid [E.Value (fromMaybe 0 -> maxPrio)] <- E.select . E.from $ \courseApplication -> do E.where_ $ courseApplication E.^. CourseApplicationCourse E.==. E.val cid E.&&. courseApplication E.^. CourseApplicationUser E.==. E.val uid - E.&&. courseApplication E.^. CourseApplicationAllocation E.==. E.just (E.val aId) + E.&&. courseApplication E.^. CourseApplicationAllocation E.==. E.val maId E.&&. E.not_ (E.isNothing $ courseApplication E.^. CourseApplicationAllocationPriority) return . E.joinV . E.max_ $ courseApplication E.^. CourseApplicationAllocationPriority return (mApplication, coursesNum, course, maxPrio) @@ -112,18 +110,20 @@ applicationForm aId cid uid ApplicationFormMode{..} csrf = do } prioField = selectField' (Just $ SomeMessage MsgAllocationCourseNoApplication) $ return prioOptions - (prioRes, prioView) <- case (afmApplicant, afmApplicantEdit, mApp) of - (True , True , Nothing) + (prioRes, prioView) <- case (isAlloc, afmApplicant, afmApplicantEdit, mApp) of + (True , True , True , Nothing) -> over _2 Just <$> mopt prioField (fslI MsgApplicationPriority) (Just $ oldPrio) - (True , True , Just _ ) + (True , True , True , Just _ ) -> over (_1 . _FormSuccess) Just . over _2 Just <$> mreq prioField (fslI MsgApplicationPriority) oldPrio - (True , False, _ ) + (True , True , False, _ ) -> over _2 Just <$> mforcedOpt prioField (fslI MsgApplicationPriority) oldPrio - (False, _ , Just _ ) + (True , False, _ , Just _ ) | is _Just oldPrio -> pure (FormSuccess oldPrio, Nothing) - _other + (True , _ , _ , _ ) -> throwM ApplicationFormNoApplication + (False, _ , _ , _ ) + -> pure (FormSuccess Nothing, Nothing) (fieldRes, fieldView') <- if | afmApplicantEdit || afmLecturer @@ -212,6 +212,15 @@ applicationForm aId cid uid ApplicationFormMode{..} csrf = do ] (actionRes, buttonsView) <- buttonForm' buttons csrf + ratingSection <- if + | afmLecturer + , afmApplicantEdit + -> Just . set _fvTooltip (Just . toHtml $ mr MsgApplicationRatingSectionSelfTip) . snd <$> formSection MsgApplicationRatingSection + | afmLecturer + -> Just . snd <$> formSection MsgApplicationRatingSection + | otherwise + -> return Nothing + return ( ApplicationForm <$> prioRes <*> fieldRes @@ -229,7 +238,8 @@ applicationForm aId cid uid ApplicationFormMode{..} csrf = do , filesLinkView , filesWarningView ] ++ maybe [] (map Just) filesView ++ - [ vetoView + [ ratingSection + , vetoView , pointsView , commentView ] @@ -240,7 +250,7 @@ applicationForm aId cid uid ApplicationFormMode{..} csrf = do -editApplicationR :: AllocationId +editApplicationR :: Maybe AllocationId -> UserId -> CourseId -> Maybe CourseApplicationId @@ -248,10 +258,10 @@ editApplicationR :: AllocationId -> (AllocationApplicationButton -> Bool) -> SomeRoute UniWorX -> Handler (ApplicationFormView, Enctype) -editApplicationR aId uid cid mAppId afMode allowAction postAction = do +editApplicationR maId uid cid mAppId afMode allowAction postAction = do Course{..} <- runDB $ get404 cid - ((appRes, appView), appEnc) <- runFormPost $ applicationForm aId cid uid afMode + ((appRes, appView), appEnc) <- runFormPost $ applicationForm maId cid uid afMode formResult appRes $ \ApplicationForm{..} -> do if @@ -260,7 +270,7 @@ editApplicationR aId uid cid mAppId afMode allowAction postAction = do -> runDB $ do haveOld <- exists [ CourseApplicationCourse ==. cid , CourseApplicationUser ==. uid - , CourseApplicationAllocation ==. Just aId + , CourseApplicationAllocation ==. maId ] when haveOld $ invalidArgsI [MsgCourseApplicationExists] @@ -276,7 +286,7 @@ editApplicationR aId uid cid mAppId afMode allowAction postAction = do , courseApplicationRatingVeto = afRatingVeto , courseApplicationRatingPoints = afRatingPoints , courseApplicationRatingComment = afRatingComment - , courseApplicationAllocation = Just aId + , courseApplicationAllocation = maId , courseApplicationAllocationPriority = afPriority , courseApplicationTime = now , courseApplicationRatingTime = guardOn rated now @@ -330,7 +340,7 @@ editApplicationR aId uid cid mAppId afMode allowAction postAction = do , courseApplicationRatingVeto = afRatingVeto , courseApplicationRatingPoints = afRatingPoints , courseApplicationRatingComment = afRatingComment - , courseApplicationAllocation = Just aId + , courseApplicationAllocation = maId , courseApplicationAllocationPriority = afPriority } @@ -395,50 +405,6 @@ postAApplyR tid ssh ash cID = do , afmLecturer } - void . editApplicationR aId uid cid Nothing afMode (== BtnAllocationApply) . SomeRoute $ AllocationR tid ssh ash AShowR :#: cID + void . editApplicationR (Just aId) uid cid Nothing afMode (== BtnAllocationApply) . SomeRoute $ AllocationR tid ssh ash AShowR :#: cID invalidArgs ["Application form required"] - - -getAApplicationR, postAApplicationR :: TermId -> SchoolId -> AllocationShorthand -> CryptoFileNameCourseApplication -> Handler Html -getAApplicationR = postAApplicationR -postAApplicationR tid ssh ash cID = do - uid <- requireAuthId - appId <- decrypt cID - (Entity aId Allocation{..}, Entity cid Course{..}, CourseApplication{..}, isAdmin, User{..}) <- runDB $ do - alloc <- getBy404 $ TermSchoolAllocationShort tid ssh ash - app <- get404 appId - Just course <- getEntity $ courseApplicationCourse app - Just appUser <- get $ courseApplicationUser app - isAdmin <- exists [UserAdminUser ==. uid, UserAdminSchool ==. alloc ^. _entityVal . _allocationSchool] - return (alloc, course, app, isAdmin, appUser) - - afmLecturer <- hasWriteAccessTo $ CourseR courseTerm courseSchool courseShorthand CEditR - afmApplicantEdit <- hasWriteAccessTo . AllocationR tid ssh ash $ AApplicationR cID - courseCID <- encrypt cid :: Handler CryptoUUIDCourse - - let afMode = ApplicationFormMode - { afmApplicant = uid == courseApplicationUser || isAdmin - , afmApplicantEdit - , afmLecturer - } - - (ApplicationFormView{..}, appEnc) <- editApplicationR aId uid cid (Just appId) afMode (/= BtnAllocationApply) $ if - | uid == courseApplicationUser - -> SomeRoute $ AllocationR tid ssh ash AShowR :#: courseCID - | otherwise - -> SomeRoute . AllocationR tid ssh ash $ AApplicationR cID - - let title = MsgCourseApplicationTitle userDisplayName courseShorthand - - siteLayoutMsg title $ do - setTitleI title - - wrapForm ((<> snd afvButtons) . renderFieldViews FormStandard . maybe id (:) afvPriority$ afvForm) FormSettings - { formMethod = POST - , formAction = Just . SomeRoute . AllocationR tid ssh ash $ AApplicationR cID - , formEncoding = appEnc - , formAttrs = [] - , formSubmit = FormNoSubmit - , formAnchor = Nothing :: Maybe Text - } diff --git a/src/Handler/Allocation/Info.hs b/src/Handler/Allocation/Info.hs new file mode 100644 index 000000000..f6aacf063 --- /dev/null +++ b/src/Handler/Allocation/Info.hs @@ -0,0 +1,13 @@ +module Handler.Allocation.Info + ( getInfoAllocationR + ) where + +import Import +import Handler.Utils + + +getInfoAllocationR :: Handler Html +getInfoAllocationR = + siteLayoutMsg MsgMenuAllocationInfo $ do + setTitleI MsgMenuAllocationInfo + $(i18nWidgetFile "allocation-info") diff --git a/src/Handler/Allocation/List.hs b/src/Handler/Allocation/List.hs new file mode 100644 index 000000000..38069fc4c --- /dev/null +++ b/src/Handler/Allocation/List.hs @@ -0,0 +1,89 @@ +module Handler.Allocation.List + ( getAllocationListR + ) where + +import Import + +import qualified Database.Esqueleto as E +import Handler.Utils.Table.Columns +import Handler.Utils.Table.Pagination + + +type AllocationTableExpr = E.SqlExpr (Entity Allocation) +type AllocationTableData = DBRow (Entity Allocation) + +allocationListIdent :: Text +allocationListIdent = "allocations" + +queryAllocation :: Getter AllocationTableExpr (E.SqlExpr (Entity Allocation)) +queryAllocation = id + +resultAllocation :: Lens' AllocationTableData (Entity Allocation) +resultAllocation = _dbrOutput + +allocationTermLink :: TermId -> SomeRoute UniWorX +allocationTermLink tid = SomeRoute (AllocationListR, [(dbFilterKey allocationListIdent "term", toPathPiece tid)]) + +allocationSchoolLink :: SchoolId -> SomeRoute UniWorX +allocationSchoolLink ssh = SomeRoute (AllocationListR, [(dbFilterKey allocationListIdent "school", toPathPiece ssh)]) + +allocationLink :: Allocation -> SomeRoute UniWorX +allocationLink Allocation{..} = SomeRoute $ AllocationR allocationTerm allocationSchool allocationShorthand AShowR + +getAllocationListR :: Handler Html +getAllocationListR = do + now <- liftIO getCurrentTime + let + dbtSQLQuery :: AllocationTableExpr -> E.SqlQuery _ + dbtSQLQuery = return + + dbtProj :: DBRow _ -> MaybeT (YesodDB UniWorX) AllocationTableData + dbtProj = return + + dbtRowKey = view $ queryAllocation . to (E.^. AllocationId) + + dbtColonnade :: Colonnade Sortable _ _ + dbtColonnade = mconcat + [ anchorColonnade (views (resultAllocation . _entityVal . _allocationTerm) allocationTermLink) $ colTermShort (resultAllocation . _entityVal . _allocationTerm) + , anchorColonnade (views (resultAllocation . _entityVal . _allocationSchool) allocationSchoolLink) $ colSchoolShort (resultAllocation . _entityVal . _allocationSchool) + , anchorColonnade (views (resultAllocation . _entityVal) allocationLink) $ colAllocationName (resultAllocation . _entityVal . _allocationName) + ] + + dbtSorting = mconcat + [ sortTerm $ queryAllocation . to (E.^. AllocationTerm) + , sortSchool $ queryAllocation . to (E.^. AllocationSchool) + , sortAllocationName $ queryAllocation . to (E.^. AllocationName) + ] + + dbtFilter = mconcat + [ fltrAllocationActive now queryAllocation + , fltrTerm $ queryAllocation . to (E.^. AllocationTerm) + , fltrSchool $ queryAllocation . to (E.^. AllocationSchool) + , fltrAllocation queryAllocation + ] + dbtFilterUI = mconcat + [ fltrAllocationActiveUI + , fltrTermUI + , fltrSchoolUI + , fltrAllocationUI + ] + + dbtStyle = def + { dbsFilterLayout = defaultDBSFilterLayout + } + dbtParams = def + + dbtCsvEncode = noCsvEncode + dbtCsvDecode = Nothing + + dbtIdent = allocationListIdent + + psValidator :: PSValidator _ _ + psValidator = def + & defaultSorting [SortDescBy "term", SortAscBy "school", SortAscBy "allocation"] + + table <- runDB $ dbTableWidget' psValidator DBTable{..} + + siteLayoutMsg MsgAllocationListTitle $ do + setTitleI MsgAllocationListTitle + table diff --git a/src/Handler/Allocation/Register.hs b/src/Handler/Allocation/Register.hs index 9c2256695..eb5e55255 100644 --- a/src/Handler/Allocation/Register.hs +++ b/src/Handler/Allocation/Register.hs @@ -8,8 +8,6 @@ module Handler.Allocation.Register import Import -import Utils.Lens - import Handler.Utils.Form {-# ANN module ("HLint: ignore Use newtype instead of data"::String) #-} diff --git a/src/Handler/Allocation/Show.hs b/src/Handler/Allocation/Show.hs index 53149712a..b31ae9273 100644 --- a/src/Handler/Allocation/Show.hs +++ b/src/Handler/Allocation/Show.hs @@ -4,24 +4,24 @@ module Handler.Allocation.Show import Import import Handler.Utils -import Utils.Lens - + import Handler.Allocation.Register import Handler.Allocation.Application import qualified Database.Esqueleto as E - + getAShowR :: TermId -> SchoolId -> AllocationShorthand -> Handler Html getAShowR tid ssh ash = do muid <- maybeAuthId + now <- liftIO getCurrentTime let resultCourse :: Simple Field1 a (Entity Course) => Lens' a (Entity Course) resultCourse = _1 resultCourseApplication :: Simple Field2 a (Maybe (Entity CourseApplication)) => Traversal' a (Entity CourseApplication) resultCourseApplication = _2 . _Just - resultHasTemplate :: Simple Field3 a (E.Value Bool) => Lens' a Bool + resultHasTemplate :: Simple Field3 a (E.Value Bool) => Lens' a Bool resultHasTemplate = _3 . _Value (Entity aId Allocation{..}, courses, registration) <- runDB $ do @@ -71,15 +71,17 @@ getAShowR tid ssh ash = do cID <- encrypt cid :: WidgetT UniWorX IO CryptoUUIDCourse mayApply <- hasWriteAccessTo . AllocationR tid ssh ash $ AApplyR cID isLecturer <- hasWriteAccessTo $ CourseR courseTerm courseSchool courseShorthand CEditR - mApplyFormView <- liftHandlerT . for muid $ \uid -> generateFormPost . applicationForm aId cid uid $ ApplicationFormMode True mayApply isLecturer - subRoute <- fmap (fromMaybe $ AApplyR cID) . for mApp $ \(Entity appId _) -> AApplicationR <$> encrypt appId + mApplyFormView <- liftHandlerT . for muid $ \uid -> generateFormPost . applicationForm (Just aId) cid uid $ ApplicationFormMode True mayApply isLecturer + tRoute <- case mApp of + Nothing -> return . AllocationR tid ssh ash $ AApplyR cID + Just (Entity appId _) -> CApplicationR courseTerm courseSchool courseShorthand <$> encrypt appId <*> pure CAEditR let mApplyFormView' = view _1 <$> mApplyFormView overrideVisible = not mayApply && is _Just mApp case mApplyFormView of Just (_, appFormEnctype) -> wrapForm $(widgetFile "allocation/show/course") FormSettings { formMethod = POST - , formAction = Just . SomeRoute $ AllocationR tid ssh ash subRoute + , formAction = Just $ SomeRoute tRoute , formEncoding = appFormEnctype , formAttrs = [ ("class", "allocation-course") ] @@ -92,5 +94,6 @@ getAShowR tid ssh ash = do
^{wdgt} |] - + let daysToRegistrationStart = assertM (>0) $ (`diffUTCTime` now) <$> allocationRegisterFrom + allocationInfoModal = modal [whamlet|_{MsgMenuAllocationInfo}|] $ Left $ SomeRoute InfoAllocationR $(widgetFile "allocation/show") diff --git a/src/Handler/Corrections.hs b/src/Handler/Corrections.hs index 3e0a5a825..b366885d3 100644 --- a/src/Handler/Corrections.hs +++ b/src/Handler/Corrections.hs @@ -12,8 +12,6 @@ import Handler.Utils.SheetType import Handler.Utils.Delete -- import Handler.Utils.Zip -import Utils.Lens - import Data.List as List (nub, foldl, foldr) import Data.Set (Set) import qualified Data.Set as Set diff --git a/src/Handler/Course/Application.hs b/src/Handler/Course/Application.hs index 7bdbb62ba..d22c299cc 100644 --- a/src/Handler/Course/Application.hs +++ b/src/Handler/Course/Application.hs @@ -1,42 +1,7 @@ module Handler.Course.Application - ( getCAFilesR - , getCApplicationsR, postCApplicationsR + ( module Handler.Course.Application ) where -import Import - -import Handler.Utils - -import qualified Database.Esqueleto as E - -import System.FilePath (addExtension) - -import qualified Data.Conduit.List as C - - -getCAFilesR :: TermId -> SchoolId -> CourseShorthand -> CryptoFileNameCourseApplication -> Handler TypedContent -getCAFilesR tid ssh csh cID = do - appId <- decrypt cID - User{..} <- runDB $ do - CourseApplication{..} <- get404 appId - Course{..} <- get404 courseApplicationCourse - let matches = and - [ tid == courseTerm - , ssh == courseSchool - , csh == courseShorthand - ] - unless matches . redirectWith movedPermanently301 $ CApplicationR courseTerm courseSchool courseShorthand cID CAFilesR - get404 courseApplicationUser - - archiveName <- fmap (flip addExtension (unpack extensionZip) . unpack) . ap getMessageRender . pure $ MsgCourseApplicationArchiveName tid ssh csh cID userDisplayName - let - fsSource = E.selectSource . E.from $ \(courseApplicationFile `E.InnerJoin` file) -> do - E.on $ courseApplicationFile E.^. CourseApplicationFileFile E.==. file E.^. FileId - E.where_ $ courseApplicationFile E.^. CourseApplicationFileApplication E.==. E.val appId - return file - - serveSomeFiles archiveName $ fsSource .| C.map entityVal - -getCApplicationsR, postCApplicationsR :: TermId -> SchoolId -> CourseShorthand -> Handler Html -getCApplicationsR = postCApplicationsR -postCApplicationsR = fail "not implemented" -- dbtable of _all_ course applications +import Handler.Course.Application.List as Handler.Course.Application +import Handler.Course.Application.Files as Handler.Course.Application +import Handler.Course.Application.Edit as Handler.Course.Application diff --git a/src/Handler/Course/Application/Edit.hs b/src/Handler/Course/Application/Edit.hs new file mode 100644 index 000000000..281a21826 --- /dev/null +++ b/src/Handler/Course/Application/Edit.hs @@ -0,0 +1,55 @@ +module Handler.Course.Application.Edit + ( getCAEditR, postCAEditR + ) where + +import Import + +import Handler.Utils +import Handler.Allocation.Application + + +getCAEditR, postCAEditR :: TermId -> SchoolId -> CourseShorthand -> CryptoFileNameCourseApplication -> Handler Html +getCAEditR = postCAEditR +postCAEditR tid ssh csh cID = do + uid <- requireAuthId + appId <- decrypt cID + (mAlloc, Entity cid Course{..}, CourseApplication{..}, isAdmin, User{..}) <- runDB $ do + course <- getBy404 $ TermSchoolCourseShort tid ssh csh + app <- get404 appId + mAlloc <- traverse getEntity404 $ courseApplicationAllocation app + appUser <- get404 $ courseApplicationUser app + isAdmin <- case mAlloc of + Just alloc -> exists [UserAdminUser ==. uid, UserAdminSchool ==. alloc ^. _entityVal . _allocationSchool] + Nothing -> exists [UserAdminUser ==. uid, UserAdminSchool ==. course ^. _entityVal . _courseSchool] + return (mAlloc, course, app, isAdmin, appUser) + + afmLecturer <- hasWriteAccessTo $ CourseR courseTerm courseSchool courseShorthand CEditR + afmApplicantEdit <- hasWriteAccessTo $ CApplicationR tid ssh csh cID CAEditR + courseCID <- encrypt cid :: Handler CryptoUUIDCourse + + let afMode = ApplicationFormMode + { afmApplicant = uid == courseApplicationUser || isAdmin + , afmApplicantEdit + , afmLecturer + } + + (ApplicationFormView{..}, appEnc) <- editApplicationR (entityKey <$> mAlloc) uid cid (Just appId) afMode (/= BtnAllocationApply) $ if + | uid == courseApplicationUser + , Just (Entity _ Allocation{..}) <- mAlloc + -> SomeRoute $ AllocationR allocationTerm allocationSchool allocationShorthand AShowR :#: courseCID + | otherwise + -> SomeRoute $ CApplicationR tid ssh csh cID CAEditR + + let title = MsgCourseApplicationTitle userDisplayName courseShorthand + + siteLayoutMsg title $ do + setTitleI title + + wrapForm ((<> snd afvButtons) . renderFieldViews FormStandard . maybe id (:) afvPriority$ afvForm) FormSettings + { formMethod = POST + , formAction = Just . SomeRoute $ CApplicationR tid ssh csh cID CAEditR + , formEncoding = appEnc + , formAttrs = [] + , formSubmit = FormNoSubmit + , formAnchor = Nothing :: Maybe Text + } diff --git a/src/Handler/Course/Application/Files.hs b/src/Handler/Course/Application/Files.hs new file mode 100644 index 000000000..31ec53e47 --- /dev/null +++ b/src/Handler/Course/Application/Files.hs @@ -0,0 +1,108 @@ +module Handler.Course.Application.Files + ( getCAFilesR + , getCAppsFilesR + ) where + +import Import +import Handler.Utils + +import System.FilePath (addExtension, ()) + +import qualified Data.Conduit.List as C + +import qualified Database.Esqueleto as E + +import qualified Data.CaseInsensitive as CI + + +getCAFilesR :: TermId -> SchoolId -> CourseShorthand -> CryptoFileNameCourseApplication -> Handler TypedContent +getCAFilesR tid ssh csh cID = do + appId <- decrypt cID + User{..} <- runDB $ do + CourseApplication{..} <- get404 appId + Course{..} <- get404 courseApplicationCourse + let matches = and + [ tid == courseTerm + , ssh == courseSchool + , csh == courseShorthand + ] + unless matches . redirectWith movedPermanently301 $ CApplicationR courseTerm courseSchool courseShorthand cID CAFilesR + get404 courseApplicationUser + + archiveName <- fmap (flip addExtension (unpack extensionZip) . unpack) . ap getMessageRender . pure $ MsgCourseApplicationArchiveName tid ssh csh cID userDisplayName + let + fsSource = E.selectSource . E.from $ \(courseApplicationFile `E.InnerJoin` file) -> do + E.on $ courseApplicationFile E.^. CourseApplicationFileFile E.==. file E.^. FileId + E.where_ $ courseApplicationFile E.^. CourseApplicationFileApplication E.==. E.val appId + return file + + serveSomeFiles archiveName $ fsSource .| C.map entityVal + + +getCAppsFilesR :: TermId -> SchoolId -> CourseShorthand -> Handler TypedContent +getCAppsFilesR tid ssh csh = do + runDB . existsBy404 $ TermSchoolCourseShort tid ssh csh + MsgRenderer mr <- getMsgRenderer + + archiveName <- fmap (flip addExtension (unpack extensionZip) . unpack) . ap getMessageRender . pure $ MsgCourseAllApplicationsArchiveName tid ssh csh + + let + fsSource :: Source DB File + fsSource = do + apps <- lift . E.select . E.from $ \((course `E.InnerJoin` courseApplication `E.InnerJoin` user) `E.LeftOuterJoin` allocation) -> do + E.on $ allocation E.?. AllocationId E.==. courseApplication E.^. CourseApplicationAllocation + E.on $ user E.^. UserId E.==. courseApplication E.^. CourseApplicationUser + E.on $ course E.^. CourseId E.==. courseApplication E.^. CourseApplicationCourse + E.where_ $ course E.^. CourseTerm E.==. E.val tid + E.&&. course E.^. CourseSchool E.==. E.val ssh + E.&&. course E.^. CourseShorthand E.==. E.val csh + return (allocation, user, courseApplication) + apps' <- flip filterM apps $ \(_, _, Entity appId _) -> do + cID <- cachedByBinary appId $ encrypt appId + hasReadAccessTo $ CApplicationR tid ssh csh cID CAFilesR + let + applicationAllocs = setOf (folded . _1) apps' + + allocations = applicationAllocs ^.. folded . _Just . _entityVal . $(multifocusG 3) _allocationTerm _allocationSchool _allocationShorthand + + allEqualOn :: Eq x => Getter _ x -> Bool + allEqualOn l = maybe True (\x -> allOf (folded . l) (== x) allocations) (allocations ^? _head . l) + + mkAllocationDir mbAlloc + | not $ allEqualOn _1 + , Just Allocation{..} <- mbAlloc + = () $ unpack [st|#{CI.foldCase (termToText (unTermKey allocationTerm))}-#{CI.foldedCase (unSchoolKey allocationSchool)}-#{CI.foldedCase allocationShorthand}|] + | not $ allEqualOn _2 + , Just Allocation{..} <- mbAlloc + = () $ unpack [st|#{CI.foldedCase (unSchoolKey allocationSchool)}-#{CI.foldedCase allocationShorthand}|] + | not $ allEqualOn _3 + , Just Allocation{..} <- mbAlloc + = () . unpack $ CI.foldedCase allocationShorthand + | Just Allocation{} <- mbAlloc + , not $ all (is _Just) applicationAllocs + = () . unpack $ mr MsgCourseApplicationsAllocatedDirectory + | Nothing <- mbAlloc + , any (is _Just) applicationAllocs + = () . unpack $ mr MsgCourseApplicationsNotAllocatedDirectory + | otherwise + = id + + forM_ apps' $ \(mbAlloc, Entity _ User{..}, Entity appId CourseApplication{..}) -> do + cID <- cachedByBinary appId $ encrypt appId :: _ CryptoFileNameCourseApplication + let mkAppDir = mkAllocationDir (entityVal <$> mbAlloc) . () (unpack [st|#{CI.foldedCase $ ciphertext cID}-#{CI.foldCase userSurname}|]) + dirFiles = C.map $ over _fileTitle mkAppDir . entityVal + fileEntitySource = E.selectSource . E.from $ \(courseApplicationFile `E.InnerJoin` file) -> do + E.on $ courseApplicationFile E.^. CourseApplicationFileFile E.==. file E.^. FileId + E.where_ $ courseApplicationFile E.^. CourseApplicationFileApplication E.==. E.val appId + return file + + yield File + { fileModified = courseApplicationTime + , fileTitle = mkAppDir "" + , fileContent = Nothing + } + + fileEntitySource .| dirFiles + + + serveSomeFiles archiveName fsSource diff --git a/src/Handler/Course/Application/List.hs b/src/Handler/Course/Application/List.hs new file mode 100644 index 000000000..a3faa9a89 --- /dev/null +++ b/src/Handler/Course/Application/List.hs @@ -0,0 +1,535 @@ +{-# LANGUAGE GeneralizedNewtypeDeriving #-} + +module Handler.Course.Application.List + ( getCApplicationsR, postCApplicationsR + ) where + +import Import + +import Handler.Utils +import Handler.Utils.Table.Columns + +import qualified Database.Esqueleto as E +import qualified Database.Esqueleto.Utils as E +import Database.Esqueleto.Utils.TH + +import qualified Data.Csv as Csv + +import qualified Data.Text as Text +import qualified Data.Text.Lens as Text + +import Data.CaseInsensitive (CI) +import qualified Data.CaseInsensitive as CI + +import qualified Data.Map as Map + +import qualified Data.Conduit.List as C + + +type CourseApplicationsTableExpr = ( E.SqlExpr (Entity CourseApplication) + `E.InnerJoin` E.SqlExpr (Entity User) + ) + `E.LeftOuterJoin` E.SqlExpr (Maybe (Entity Allocation)) + `E.LeftOuterJoin` ( E.SqlExpr (Maybe (Entity StudyFeatures)) + `E.InnerJoin` E.SqlExpr (Maybe (Entity StudyTerms)) + `E.InnerJoin` E.SqlExpr (Maybe (Entity StudyDegree)) + ) +type CourseApplicationsTableData = DBRow ( Entity CourseApplication + , Entity User + , E.Value Bool -- hasFiles + , Maybe (Entity Allocation) + , Maybe (Entity StudyFeatures) + , Maybe (Entity StudyTerms) + , Maybe (Entity StudyDegree) + ) + +courseApplicationsIdent :: Text +courseApplicationsIdent = "applications" + +queryCourseApplication :: Getter CourseApplicationsTableExpr (E.SqlExpr (Entity CourseApplication)) +queryCourseApplication = to $ $(sqlIJproj 2 1) . $(sqlLOJproj 3 1) + +queryUser :: Getter CourseApplicationsTableExpr (E.SqlExpr (Entity User)) +queryUser = to $ $(sqlIJproj 2 2) . $(sqlLOJproj 3 1) + +queryHasFiles :: Getter CourseApplicationsTableExpr (E.SqlExpr (E.Value Bool)) +queryHasFiles = to $ hasFiles . $(sqlIJproj 2 1) . $(sqlLOJproj 3 1) + where + hasFiles appl = E.exists . E.from $ \courseApplicationFile -> + E.where_ $ courseApplicationFile E.^. CourseApplicationFileApplication E.==. appl E.^. CourseApplicationId + +queryAllocation :: Getter CourseApplicationsTableExpr (E.SqlExpr (Maybe (Entity Allocation))) +queryAllocation = to $(sqlLOJproj 3 2) + +queryStudyFeatures :: Getter CourseApplicationsTableExpr (E.SqlExpr (Maybe (Entity StudyFeatures))) +queryStudyFeatures = to $ $(sqlIJproj 3 1) . $(sqlLOJproj 3 3) + +queryStudyTerms :: Getter CourseApplicationsTableExpr (E.SqlExpr (Maybe (Entity StudyTerms))) +queryStudyTerms = to $ $(sqlIJproj 3 2) . $(sqlLOJproj 3 3) + +queryStudyDegree :: Getter CourseApplicationsTableExpr (E.SqlExpr (Maybe (Entity StudyDegree))) +queryStudyDegree = to $ $(sqlIJproj 3 3) . $(sqlLOJproj 3 3) + +resultCourseApplication :: Lens' CourseApplicationsTableData (Entity CourseApplication) +resultCourseApplication = _dbrOutput . _1 + +resultUser :: Lens' CourseApplicationsTableData (Entity User) +resultUser = _dbrOutput . _2 + +resultHasFiles :: Lens' CourseApplicationsTableData Bool +resultHasFiles = _dbrOutput . _3 . _Value + +resultAllocation :: Traversal' CourseApplicationsTableData (Entity Allocation) +resultAllocation = _dbrOutput . _4 . _Just + +resultStudyFeatures :: Traversal' CourseApplicationsTableData (Entity StudyFeatures) +resultStudyFeatures = _dbrOutput . _5 . _Just + +resultStudyTerms :: Traversal' CourseApplicationsTableData (Entity StudyTerms) +resultStudyTerms = _dbrOutput . _6 . _Just + +resultStudyDegree :: Traversal' CourseApplicationsTableData (Entity StudyDegree) +resultStudyDegree = _dbrOutput . _7 . _Just + + +newtype CourseApplicationsTableVeto = CourseApplicationsTableVeto Bool + deriving (Eq, Ord, Read, Show, Generic, Typeable) + deriving newtype (Enum, Bounded) +makePrisms ''CourseApplicationsTableVeto + +instance Csv.ToField CourseApplicationsTableVeto where + toField (CourseApplicationsTableVeto True) = "veto" + toField (CourseApplicationsTableVeto False) = "" + +instance Csv.FromField CourseApplicationsTableVeto where + parseField f = do + (CI.map Text.strip -> t :: CI Text) <- Csv.parseField f + return . CourseApplicationsTableVeto $ any (== t) + [ "veto", "v", "yes", "y", "ja", "j", "wahr", "w", "true", "t", "1" ] + +data CourseApplicationsTableCsv = CourseApplicationsTableCsv + { csvCAAllocation :: Maybe AllocationShorthand + , csvCAApplication :: Maybe CryptoFileNameCourseApplication + , csvCAName :: Maybe Text + , csvCAMatriculation :: Maybe Text + , csvCAField :: Maybe Text + , csvCADegree :: Maybe Text + , csvCASemester :: Maybe Int + , csvCAText :: Maybe Text + , csvCAHasFiles :: Maybe Bool + , csvCAVeto :: Maybe CourseApplicationsTableVeto + , csvCARating :: Maybe ExamGrade + , csvCAComment :: Maybe Text + } deriving (Generic) +makeLenses_ ''CourseApplicationsTableCsv + +courseApplicationsTableCsvOptions :: Csv.Options +courseApplicationsTableCsvOptions = Csv.defaultOptions { Csv.fieldLabelModifier = camelToPathPiece' 2 } + +instance Csv.ToNamedRecord CourseApplicationsTableCsv where + toNamedRecord = Csv.genericToNamedRecord courseApplicationsTableCsvOptions + +instance Csv.FromNamedRecord CourseApplicationsTableCsv where + parseNamedRecord csv + = CourseApplicationsTableCsv + <$> csv .:?? "allocation" + <*> csv .:?? "application" + <*> csv .:?? "name" + <*> csv .:?? "matriculation" + <*> csv .:?? "field" + <*> csv .:?? "degree" + <*> csv .:?? "semester" + <*> csv .:?? "text" + <*> csv .:?? "has-files" + <*> csv .:?? "veto" + <*> csv .:?? "rating" + <*> csv .:?? "comment" + +instance Csv.DefaultOrdered CourseApplicationsTableCsv where + headerOrder = Csv.genericHeaderOrder courseApplicationsTableCsvOptions + +instance CsvColumnsExplained CourseApplicationsTableCsv where + csvColumnsExplanations = genericCsvColumnsExplanations courseApplicationsTableCsvOptions $ Map.fromList + [ ('csvCAAllocation , MsgCsvColumnApplicationsAllocation ) + , ('csvCAApplication , MsgCsvColumnApplicationsApplication ) + , ('csvCAName , MsgCsvColumnApplicationsName ) + , ('csvCAMatriculation, MsgCsvColumnApplicationsMatriculation) + , ('csvCAField , MsgCsvColumnApplicationsField ) + , ('csvCADegree , MsgCsvColumnApplicationsDegree ) + , ('csvCASemester , MsgCsvColumnApplicationsSemester ) + , ('csvCAText , MsgCsvColumnApplicationsText ) + , ('csvCAHasFiles , MsgCsvColumnApplicationsHasFiles ) + , ('csvCAVeto , MsgCsvColumnApplicationsVeto ) + , ('csvCARating , MsgCsvColumnApplicationsRating ) + , ('csvCAComment , MsgCsvColumnApplicationsComment ) + ] + +data CourseApplicationsTableCsvActionClass + = CourseApplicationsTableCsvSetField + | CourseApplicationsTableCsvSetVeto + | CourseApplicationsTableCsvSetRating + | CourseApplicationsTableCsvSetComment + deriving (Eq, Ord, Read, Show, Enum, Bounded, Generic, Typeable) +embedRenderMessage ''UniWorX ''CourseApplicationsTableCsvActionClass id + +data CourseApplicationsTableCsvAction + = CourseApplicationsTableCsvSetFieldData + { caCsvActApplication :: CourseApplicationId + , caCsvActField :: Maybe StudyFeaturesId + } + | CourseApplicationsTableCsvSetVetoData + { caCsvActApplication :: CourseApplicationId + , caCsvActVeto :: Bool + } + | CourseApplicationsTableCsvSetRatingData + { caCsvActApplication :: CourseApplicationId + , caCsvActRating :: Maybe ExamGrade + } + | CourseApplicationsTableCsvSetCommentData + { caCsvActApplication :: CourseApplicationId + , caCsvActComment :: Maybe Text + } + deriving (Eq, Ord, Read, Show, Generic, Typeable) +deriveJSON defaultOptions + { constructorTagModifier = over Text.packed $ Text.intercalate "-" . map Text.toLower . drop 4 . dropEnd 1 . splitCamel + , fieldLabelModifier = camelToPathPiece' 3 + , sumEncoding = TaggedObject "action" "data" + } ''CourseApplicationsTableCsvAction + +data CourseApplicationsTableCsvException + = CourseApplicationsTableCsvExceptionNoMatchingUser + | CourseApplicationsTableCsvExceptionNoMatchingAllocation + | CourseApplicationsTableCsvExceptionNoMatchingStudyFeatures + deriving (Show, Generic, Typeable) + +instance Exception CourseApplicationsTableCsvException + +embedRenderMessage ''UniWorX ''CourseApplicationsTableCsvException id + + +getCApplicationsR, postCApplicationsR :: TermId -> SchoolId -> CourseShorthand -> Handler Html +getCApplicationsR = postCApplicationsR +postCApplicationsR tid ssh csh = do + table <- runDB $ do + cid <- getKeyBy404 $ TermSchoolCourseShort tid ssh csh + + let + allocationLink :: Allocation -> SomeRoute UniWorX + allocationLink Allocation{..} = SomeRoute $ AllocationR allocationTerm allocationSchool allocationShorthand AShowR + + participantLink :: MonadCrypto m => UserId -> m (SomeRoute UniWorX) + participantLink uid = do + cID <- encrypt uid + return . SomeRoute . CourseR tid ssh csh $ CUserR cID + + dbtSQLQuery :: CourseApplicationsTableExpr -> E.SqlQuery _ + dbtSQLQuery = runReaderT $ do + courseApplication <- view queryCourseApplication + hasFiles <- view queryHasFiles + user <- view queryUser + allocation <- view queryAllocation + studyFeatures <- view queryStudyFeatures + studyTerms <- view queryStudyTerms + studyDegree <- view queryStudyDegree + + lift $ do + 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 + E.on $ courseApplication E.^. CourseApplicationAllocation E.==. allocation E.?. AllocationId + E.on $ user E.^. UserId E.==. courseApplication E.^. CourseApplicationUser + E.where_ $ courseApplication E.^. CourseApplicationCourse E.==. E.val cid + + return (courseApplication, user, hasFiles, allocation, studyFeatures, studyTerms, studyDegree) + + dbtProj :: DBRow _ -> MaybeT (YesodDB UniWorX) CourseApplicationsTableData + dbtProj = runReaderT $ do + appId <- view $ resultCourseApplication . _entityKey + cID <- encrypt appId + + guardM . hasReadAccessTo $ CApplicationR tid ssh csh cID CAEditR + + view id + + dbtRowKey = view $ queryCourseApplication . to (E.^. CourseApplicationId) + + dbtColonnade :: Colonnade Sortable _ _ + dbtColonnade = mconcat + [ emptyOpticColonnade (resultAllocation . _entityVal) $ \l -> anchorColonnade (views l allocationLink) $ colAllocationShorthand (l . _allocationShorthand) + , colApplicationId (resultCourseApplication . _entityKey) + , anchorColonnadeM (views (resultUser . _entityKey) participantLink) $ colUserDisplayName (resultUser . _entityVal . $(multifocusL 2) _userDisplayName _userSurname) + , colUserMatriculation (resultUser . _entityVal . _userMatrikelnummer) + , emptyOpticColonnade (resultStudyTerms . _entityVal) colStudyTerms + , emptyOpticColonnade (resultStudyDegree . _entityVal) colStudyDegree + , emptyOpticColonnade (resultStudyFeatures . _entityVal . _studyFeaturesSemester) colStudyFeaturesSemester + , colApplicationText (resultCourseApplication . _entityVal . _courseApplicationText) + , lmap ((tid, ssh, csh), ) $ colApplicationFiles ($(multifocusL 5) (_1 . _1) (_1 . _2) (_1 . _3) (_2 . resultCourseApplication . _entityKey) (_2 . resultHasFiles)) + , colApplicationVeto (resultCourseApplication . _entityVal . _courseApplicationRatingVeto) + , colApplicationRatingPoints (resultCourseApplication . _entityVal . _courseApplicationRatingPoints) + , colApplicationRatingComment (resultCourseApplication . _entityVal . _courseApplicationRatingComment) + ] + + dbtSorting = mconcat + [ sortAllocationShorthand $ queryAllocation . to (E.?. AllocationShorthand) + , sortUserName' $ $(multifocusG 2) (queryUser . to (E.^. UserDisplayName)) (queryUser . to (E.^. UserSurname)) + , sortUserMatriculation $ queryUser . to (E.^. UserMatrikelnummer) + , sortStudyTerms queryStudyTerms + , sortStudyDegree queryStudyDegree + , sortStudyFeaturesSemester $ queryStudyFeatures . to (E.?. StudyFeaturesSemester) + , sortApplicationText $ queryCourseApplication . to (E.^. CourseApplicationText) + , sortApplicationFiles queryHasFiles + , sortApplicationVeto $ queryCourseApplication . to (E.^. CourseApplicationRatingVeto) + , sortApplicationRatingPoints $ queryCourseApplication . to (E.^. CourseApplicationRatingPoints) + , sortApplicationRatingComment $ queryCourseApplication . to (E.^. CourseApplicationRatingComment) + ] + + dbtFilter = mconcat + [ fltrAllocation queryAllocation + , fltrUserName' $ queryUser . to (E.^. UserDisplayName) + , fltrUserMatriculation $ queryUser . to (E.^. UserMatrikelnummer) + , fltrStudyTerms queryStudyTerms + , fltrStudyDegree queryStudyDegree + , fltrStudyFeaturesSemester $ queryStudyFeatures . to (E.?. StudyFeaturesSemester) + , fltrApplicationText $ queryCourseApplication . to (E.^. CourseApplicationText) + , fltrApplicationFiles queryHasFiles + , fltrApplicationVeto $ queryCourseApplication . to (E.^. CourseApplicationRatingVeto) + , fltrApplicationRatingPoints $ queryCourseApplication . to (E.^. CourseApplicationRatingPoints) + , fltrApplicationRatingComment $ queryCourseApplication . to (E.^. CourseApplicationRatingComment) + ] + dbtFilterUI = mconcat + [ fltrAllocationUI + , fltrUserNameUI' + , fltrUserMatriculationUI + , fltrStudyTermsUI + , fltrStudyDegreeUI + , fltrStudyFeaturesSemesterUI + , fltrApplicationTextUI + , fltrApplicationFilesUI + , fltrApplicationVetoUI + , fltrApplicationRatingPointsUI + , fltrApplicationRatingCommentUI + ] + + dbtStyle = def + { dbsFilterLayout = defaultDBSFilterLayout + } + dbtParams = def + + dbtCsvEncode :: DBTCsvEncode CourseApplicationsTableData CourseApplicationsTableCsv + dbtCsvEncode = DictJust . C.mapM . runReaderT $ CourseApplicationsTableCsv + <$> preview (resultAllocation . _entityVal . _allocationShorthand) + <*> (preview (resultCourseApplication . _entityKey) >>= traverse encrypt) + <*> preview (resultUser . _entityVal . _userDisplayName) + <*> preview (resultUser . _entityVal . _userMatrikelnummer . _Just) + <*> preview (resultStudyTerms . _entityVal . (_studyTermsName . _Just <> _studyTermsShorthand . _Just <> to (tshow . studyTermsKey))) + <*> preview (resultStudyDegree . _entityVal . (_studyDegreeName . _Just <> _studyDegreeShorthand . _Just <> to (tshow . studyDegreeKey))) + <*> preview (resultStudyFeatures . _entityVal . _studyFeaturesSemester) + <*> preview (resultCourseApplication . _entityVal . _courseApplicationText . _Just) + <*> preview resultHasFiles + <*> preview (resultCourseApplication . _entityVal . _courseApplicationRatingVeto . re _CourseApplicationsTableVeto) + <*> preview (resultCourseApplication . _entityVal . _courseApplicationRatingPoints . _Just) + <*> preview (resultCourseApplication . _entityVal . _courseApplicationRatingComment . _Just) + dbtCsvDecode = Just DBTCsvDecode + { dbtCsvRowKey = \csv -> do + appRes <- lift $ guessUser csv + case appRes of + Right appId -> return $ E.Value appId + Left uid -> do + alloc <- lift $ guessAllocation csv + [appId] <- lift $ selectKeysList [CourseApplicationUser ==. uid, CourseApplicationAllocation ==. alloc] [LimitTo 2] + return $ E.Value appId + , dbtCsvComputeActions = \case + DBCsvDiffMissing{} + -> return () -- no deletion + DBCsvDiffNew{} + -> return () -- no addition + DBCsvDiffExisting{..} -> do + let appId = dbCsvOld ^. resultCourseApplication . _entityKey + + newFeatures <- lift $ lookupStudyFeatures dbCsvNew + when (newFeatures /= dbCsvOld ^? resultStudyFeatures . _entityKey) $ + yield $ CourseApplicationsTableCsvSetFieldData appId newFeatures + + let mVeto = dbCsvNew ^? _csvCAVeto . _Just . _CourseApplicationsTableVeto + whenIsJust mVeto $ \veto -> + when (veto /= dbCsvOld ^. resultCourseApplication . _entityVal . _courseApplicationRatingVeto) $ + yield $ CourseApplicationsTableCsvSetVetoData appId veto + + when (dbCsvNew ^. _csvCARating /= dbCsvOld ^. resultCourseApplication . _entityVal . _courseApplicationRatingPoints) $ + yield $ CourseApplicationsTableCsvSetRatingData appId (dbCsvNew ^. _csvCARating) + + when (dbCsvNew ^. _csvCAComment /= dbCsvOld ^. resultCourseApplication . _entityVal . _courseApplicationRatingComment) $ + yield $ CourseApplicationsTableCsvSetCommentData appId (dbCsvNew ^. _csvCAComment) + , dbtCsvClassifyAction = \case + CourseApplicationsTableCsvSetFieldData{} -> CourseApplicationsTableCsvSetField + CourseApplicationsTableCsvSetVetoData{} -> CourseApplicationsTableCsvSetVeto + CourseApplicationsTableCsvSetRatingData{} -> CourseApplicationsTableCsvSetRating + CourseApplicationsTableCsvSetCommentData{} -> CourseApplicationsTableCsvSetComment + , dbtCsvCoarsenActionClass = const DBCsvActionExisting + , dbtCsvExecuteActions = do + now <- liftIO getCurrentTime + C.mapM_ $ \case + CourseApplicationsTableCsvSetFieldData{..} -> do + CourseApplication{..} <- updateGet caCsvActApplication [ CourseApplicationField =. caCsvActField + , CourseApplicationTime =. now + ] + audit $ TransactionCourseApplicationEdit cid courseApplicationUser caCsvActApplication + CourseApplicationsTableCsvSetVetoData{..} -> do + CourseApplication{..} <- updateGet caCsvActApplication [ CourseApplicationRatingVeto =. caCsvActVeto + , CourseApplicationRatingTime =. Just now + ] + audit $ TransactionCourseApplicationEdit cid courseApplicationUser caCsvActApplication + CourseApplicationsTableCsvSetRatingData{..} -> do + CourseApplication{..} <- updateGet caCsvActApplication [ CourseApplicationRatingPoints =. caCsvActRating + , CourseApplicationRatingTime =. Just now + ] + audit $ TransactionCourseApplicationEdit cid courseApplicationUser caCsvActApplication + CourseApplicationsTableCsvSetCommentData{..} -> do + CourseApplication{..} <- updateGet caCsvActApplication [ CourseApplicationRatingComment =. caCsvActComment + , CourseApplicationRatingTime =. Just now + ] + audit $ TransactionCourseApplicationEdit cid courseApplicationUser caCsvActApplication + return $ CourseR tid ssh csh CApplicationsR + , dbtCsvRenderKey = \(existingApplicantName -> existingApplicantName') -> \case + CourseApplicationsTableCsvSetFieldData{..} -> + [whamlet| + $newline never + ^{existingApplicantName' caCsvActApplication} + $maybe features <- caCsvActField + , ^{studyFeaturesWidget features} + $nothing + , _{MsgCourseStudyFeatureNone} + |] + CourseApplicationsTableCsvSetVetoData{..} -> + [whamlet| + $newline never + ^{existingApplicantName' caCsvActApplication} + $if caCsvActVeto + , _{MsgCourseApplicationVeto} + $else + , _{MsgCourseApplicationNoVeto} + |] + CourseApplicationsTableCsvSetRatingData{..} -> + [whamlet| + $newline never + ^{existingApplicantName' caCsvActApplication} + $maybe newResult <- caCsvActRating + , _{newResult} + $nothing + , _{MsgCourseApplicationNoRatingPoints} + |] + CourseApplicationsTableCsvSetCommentData{..} -> + [whamlet| + $newline never + ^{existingApplicantName' caCsvActApplication} + $if is _Nothing caCsvActComment + , _{MsgCourseApplicationNoRatingComment} + |] + , dbtCsvRenderActionClass = toWidget <=< ap getMessageRender . pure + , dbtCsvRenderException = ap getMessageRender . pure :: CourseApplicationsTableCsvException -> DB Text + } + where + guessUser :: CourseApplicationsTableCsv -> DB (Either UserId CourseApplicationId) + guessUser csv = do + mApp <- runMaybeT $ do + appId <- squash . catchIfMaybeT (const True :: CryptoIDError -> Bool) . MaybeT . traverse decrypt $ csv ^? _csvCAApplication . _Just + CourseApplication{..} <- MaybeT $ get appId + guard $ courseApplicationCourse == cid + return appId + + maybe (Left <$> guessUser' csv) (return . Right) mApp + where + guessUser' :: CourseApplicationsTableCsv -> DB UserId + guessUser' CourseApplicationsTableCsv{..} = $cachedHereBinary (csvCAMatriculation, csvCAName) $ do + users <- E.select . E.from $ \user -> do + E.where_ . E.and $ catMaybes + [ (user E.^. UserMatrikelnummer E.==.) . E.val . Just <$> csvCAMatriculation + , (user E.^. UserDisplayName E.==.) . E.val <$> csvCAName + ] + return $ user E.^. UserId + case users of + [E.Value uid] + -> return uid + _other + -> throwM CourseApplicationsTableCsvExceptionNoMatchingUser + + guessAllocation :: CourseApplicationsTableCsv -> DB (Maybe AllocationId) + guessAllocation CourseApplicationsTableCsv{..} = $cachedHereBinary csvCAAllocation . for csvCAAllocation $ \ash -> do + mAlloc <- traverse (getJustEntity . allocationCourseAllocation . entityVal) <=< getBy $ UniqueAllocationCourse cid + case mAlloc of + Just (Entity allocId Allocation{..}) + | allocationShorthand == ash + -> return allocId + _other + -> throwM CourseApplicationsTableCsvExceptionNoMatchingAllocation + + existingApplicantName :: Map (E.Value CourseApplicationId) CourseApplicationsTableData -> CourseApplicationId -> Widget + existingApplicantName existing (E.Value -> appId) = nameWidget userDisplayName userSurname + where + Entity _ User{..} = existing ^. singular (ix appId . resultUser) + + lookupStudyFeatures :: CourseApplicationsTableCsv -> DB (Maybe StudyFeaturesId) + lookupStudyFeatures csv@CourseApplicationsTableCsv{..} = do + appRes <- guessUser csv + (uid, oldFeatures) <- case appRes of + Left uid -> (uid, ) <$> selectList [ CourseApplicationUser ==. uid, CourseApplicationCourse ==. cid ] [] + Right appId -> (courseApplicationUser . entityVal &&& pure) <$> getJustEntity appId + studyFeatures <- E.select . E.from $ \(studyFeatures `E.InnerJoin` studyDegree `E.InnerJoin` studyTerms) -> + E.distinctOnOrderBy [ E.asc (studyFeatures E.^. StudyFeaturesField) + , E.asc (studyFeatures E.^. StudyFeaturesDegree) + , E.asc (studyFeatures E.^. StudyFeaturesSemester)] $ do + E.on $ studyTerms E.^. StudyTermsId E.==. studyFeatures E.^. StudyFeaturesField + E.on $ studyDegree E.^. StudyDegreeId E.==. studyFeatures E.^. StudyFeaturesDegree + E.where_ . E.and $ catMaybes + [ do + field <- csvCAField + return . E.or $ catMaybes + [ Just $ studyTerms E.^. StudyTermsName `E.ciEq` E.just (E.val field) + , Just $ studyTerms E.^. StudyTermsShorthand `E.ciEq` E.just (E.val field) + , (studyTerms E.^. StudyTermsKey E.==.) . E.val <$> readMay field + ] + , do + degree <- csvCADegree + return . E.or $ catMaybes + [ Just $ studyDegree E.^. StudyDegreeName `E.ciEq` E.just (E.val degree) + , Just $ studyDegree E.^. StudyDegreeShorthand `E.ciEq` E.just (E.val degree) + , (studyDegree E.^. StudyDegreeKey E.==.) . E.val <$> readMay degree + ] + , (studyFeatures E.^. StudyFeaturesSemester E.==.) . E.val <$> csvCASemester + ] + E.where_ $ studyFeatures E.^. StudyFeaturesUser E.==. E.val uid + let isActiveOrPrevious = E.or + $ (studyFeatures E.^. StudyFeaturesValid) + : [ E.val sfid E.==. studyFeatures E.^. StudyFeaturesId + | Entity _ CourseApplication{ courseApplicationField = Just sfid } <- oldFeatures + ] + E.where_ isActiveOrPrevious -- either active studyFeature or the one previously associated with this course + E.orderBy [E.desc isActiveOrPrevious, E.asc (E.orderByOrd $ studyFeatures E.^. StudyFeaturesType)] + return $ studyFeatures E.^. StudyFeaturesId + case studyFeatures of + [E.Value fid] -> return $ Just fid + _other + | is _Nothing csvCAField + , is _Nothing csvCADegree + , is _Nothing csvCASemester + -> return Nothing + _other + | [Entity _ CourseApplication{..}] <- oldFeatures + , Just sfid <- courseApplicationField + , E.Value sfid `elem` studyFeatures + -> return $ Just sfid + _other -> throwM CourseApplicationsTableCsvExceptionNoMatchingStudyFeatures + + + dbtIdent = courseApplicationsIdent + + psValidator :: PSValidator _ _ + psValidator = def + + dbTableWidget' psValidator DBTable{..} + + let title = prependCourseTitle tid ssh csh MsgCourseApplicationsListTitle + + siteLayoutMsg title $ do + setTitleI title + table diff --git a/src/Handler/Course/Edit.hs b/src/Handler/Course/Edit.hs index cdedd90bb..248c17571 100644 --- a/src/Handler/Course/Edit.hs +++ b/src/Handler/Course/Edit.hs @@ -5,7 +5,6 @@ module Handler.Course.Edit import Import -import Utils.Lens import Utils.Form import Handler.Utils import Handler.Utils.Invitations @@ -542,7 +541,15 @@ upsertAllocationCourse cid cfAllocation = do -> return True | Just Allocation{allocationStaffRegisterTo} <- prevAllocation , NTop allocationStaffRegisterTo <= NTop (Just now) - -> False <$ addMessageI Error MsgAllocationStaffRegisterToExpired + -> let anyChanges + | Just AllocationCourseForm{..} <- cfAllocation + , Just (Entity _ AllocationCourse{..}) <- prevAllocationCourse + = or [ acfAllocation /= allocationCourseAllocation + , acfMinCapacity /= allocationCourseMinCapacity + ] + | otherwise + = True + in False <$ when anyChanges (addMessageI Error MsgAllocationStaffRegisterToExpired) | otherwise -> return True diff --git a/src/Handler/Course/LecturerInvite.hs b/src/Handler/Course/LecturerInvite.hs index 25086ff1b..7bc870396 100644 --- a/src/Handler/Course/LecturerInvite.hs +++ b/src/Handler/Course/LecturerInvite.hs @@ -8,7 +8,6 @@ module Handler.Course.LecturerInvite import Import -import Utils.Lens import Utils.Form import Handler.Utils.Invitations diff --git a/src/Handler/Course/List.hs b/src/Handler/Course/List.hs index 4a29f3851..4a8a63703 100644 --- a/src/Handler/Course/List.hs +++ b/src/Handler/Course/List.hs @@ -10,7 +10,6 @@ import Import import Data.Maybe (fromJust) -import Utils.Lens import Utils.Form -- import Utils.DB import Handler.Utils @@ -26,39 +25,39 @@ import qualified Database.Esqueleto.Utils as E -- NOTE: Outdated way to use dbTable; see ProfileDataR Handler for a more recent method. -type CourseTableData = DBRow (Entity Course, Int, Bool, Entity School, [Entity User]) +type CourseTableData = DBRow (Entity Course, Int, Bool, Entity School, [Entity User], Maybe (Entity Allocation)) colCourse :: IsDBTable m a => Colonnade Sortable CourseTableData (DBCell m a) colCourse = sortable (Just "course") (i18nCell MsgCourse) - $ \DBRow{ dbrOutput=(Entity _ Course{..}, _, _, _, _) } -> + $ \DBRow{ dbrOutput=(Entity _ Course{..}, _, _, _, _, _) } -> anchorCell (CourseR courseTerm courseSchool courseShorthand CShowR) [whamlet|_{courseName}|] colDescription :: IsDBTable m a => Colonnade Sortable CourseTableData (DBCell m a) colDescription = sortable Nothing mempty - $ \DBRow{ dbrOutput=(Entity _ Course{..}, _, _, _, _) } -> + $ \DBRow{ dbrOutput=(Entity _ Course{..}, _, _, _, _, _) } -> case courseDescription of Nothing -> mempty (Just descr) -> cell $ modal (toWidget $ hasComment True) (Right $ toWidget descr) colCShort :: IsDBTable m a => Colonnade Sortable CourseTableData (DBCell m a) colCShort = sortable (Just "cshort") (i18nCell MsgCourseShort) - $ \DBRow{ dbrOutput=(Entity _ Course{..}, _, _, _, _) } -> + $ \DBRow{ dbrOutput=(Entity _ Course{..}, _, _, _, _, _) } -> anchorCell (CourseR courseTerm courseSchool courseShorthand CShowR) [whamlet|_{courseShorthand}|] colTerm :: IsDBTable m a => Colonnade Sortable CourseTableData (DBCell m a) colTerm = sortable (Just "term") (i18nCell MsgTerm) - $ \DBRow{ dbrOutput=(Entity _ Course{..}, _, _, _, _) } -> + $ \DBRow{ dbrOutput=(Entity _ Course{..}, _, _, _, _, _) } -> anchorCell (TermCourseListR courseTerm) [whamlet|#{courseTerm}|] colSchoolShort :: IsDBTable m a => Colonnade Sortable CourseTableData (DBCell m a) colSchoolShort = sortable (Just "schoolshort") (i18nCell MsgCourseSchoolShort) - $ \DBRow{ dbrOutput=(Entity _ Course{..}, _, _, Entity _ School{..}, _) } -> + $ \DBRow{ dbrOutput=(Entity _ Course{..}, _, _, Entity _ School{..}, _, _) } -> anchorCell (TermSchoolCourseListR courseTerm courseSchool) [whamlet|_{schoolShorthand}|] colRegistered :: IsDBTable m a => Colonnade Sortable CourseTableData (DBCell m a) colRegistered = sortable (Just "registered") (i18nCell MsgRegistered) - $ \DBRow{ dbrOutput=(_, _, registered, _, _) } -> tickmarkCell registered + $ \DBRow{ dbrOutput=(_, _, registered, _, _, _) } -> tickmarkCell registered type CourseTableExpr = E.SqlExpr (Entity Course) `E.InnerJoin` E.SqlExpr (Entity School) @@ -91,7 +90,9 @@ makeCourseTable whereClause colChoices psValidator = do dbtProj :: DBRow _ -> MaybeT (ReaderT SqlBackend (HandlerT UniWorX IO)) CourseTableData dbtProj = traverse $ \(course, E.Value participants, E.Value registered, school) -> do lecturerList <- lift $ E.select $ E.from $ lecturerQuery $ E.val $ entityKey course - return (course, participants, registered, school, lecturerList) + courseAlloc <- lift $ getBy (UniqueAllocationCourse $ entityKey course) + >>= traverse (getJustEntity . allocationCourseAllocation . entityVal) + return (course, participants, registered, school, lecturerList, courseAlloc) snd <$> dbTable psValidator DBTable { dbtSQLQuery , dbtRowKey = \(course `E.InnerJoin` _) -> course E.^. CourseId @@ -142,7 +143,22 @@ makeCourseTable whereClause colChoices psValidator = do Nothing -> E.val True Just b -> let regTo = course E.^. CourseRegisterTo regFrom = course E.^. CourseRegisterFrom - in (E.==.) (E.val b) $ (E.isNothing regTo E.||. E.val (Just now) E.<=. regTo) E.&&. E.val (Just now) E.>=. regFrom + courseOpen = E.maybe E.false (\f -> f E.<=. E.val now) regFrom + E.&&. E.maybe E.true (\t -> E.val now E.<=. t) regTo + alloc allocation = do + E.where_ . E.exists . E.from $ \allocationCourse -> + E.where_ $ allocationCourse E.^. AllocationCourseCourse E.==. course E.^. CourseId + return allocation + allocOpen allocation = ( E.maybe E.false (\f -> f E.<=. E.val now) (allocation E.^. AllocationRegisterFrom) + E.&&. E.maybe E.true (\t -> E.val now E.<=. t) (allocation E.^. AllocationRegisterTo) + ) + E.||. ( courseOpen + E.&&. E.maybe E.false (\f -> f E.<=. E.val now) (allocation E.^. AllocationRegisterByCourse) + ) + in (E.==. E.val b) $ ( courseOpen + E.&&. E.not_ (E.exists . void $ E.from alloc) + ) + E.||. E.exists (E.from $ E.where_ . allocOpen <=< alloc) ) , ( "registered", FilterColumn $ \tExpr criterion -> case getLast (criterion :: Last Bool) of Nothing -> E.val True :: E.SqlExpr (E.Value Bool) @@ -165,8 +181,8 @@ makeCourseTable whereClause colChoices psValidator = do ] , dbtStyle = def { dbsFilterLayout = defaultDBSFilterLayout - , dbsTemplate = DBSTCourse (_dbrOutput . _1) (_dbrOutput . _5) (_dbrOutput . _3) (_dbrOutput . _4) - -- ^ course ^ lecturer list ^ isRegistered ^ school + , dbsTemplate = DBSTCourse (_dbrOutput . _1) (_dbrOutput . _5) (_dbrOutput . _3) (_dbrOutput . _4) (_dbrOutput . _6 . _Just) + -- ^ course ^ lecturer list ^ isRegistered ^ school ^ allocation } , dbtParams = def , dbtIdent = "courses" :: Text diff --git a/src/Handler/Course/ParticipantInvite.hs b/src/Handler/Course/ParticipantInvite.hs index 292d0bf26..97b79e54c 100644 --- a/src/Handler/Course/ParticipantInvite.hs +++ b/src/Handler/Course/ParticipantInvite.hs @@ -8,7 +8,6 @@ module Handler.Course.ParticipantInvite import Import -import Utils.Lens import Utils.Form import Handler.Utils import Handler.Utils.Invitations diff --git a/src/Handler/Course/Register.hs b/src/Handler/Course/Register.hs index 7729d82f8..d134e31d1 100644 --- a/src/Handler/Course/Register.hs +++ b/src/Handler/Course/Register.hs @@ -7,7 +7,6 @@ module Handler.Course.Register import Import -import Utils.Lens import Handler.Utils import Data.Function ((&)) diff --git a/src/Handler/Course/Show.hs b/src/Handler/Course/Show.hs index 9bc1b8f53..17125062d 100644 --- a/src/Handler/Course/Show.hs +++ b/src/Handler/Course/Show.hs @@ -12,7 +12,6 @@ import qualified Database.Esqueleto.Utils as E import Database.Esqueleto.Utils.TH import qualified Data.CaseInsensitive as CI -import Utils.Lens import qualified Data.Map as Map diff --git a/src/Handler/Course/User.hs b/src/Handler/Course/User.hs index 798e23244..81ac54d5b 100644 --- a/src/Handler/Course/User.hs +++ b/src/Handler/Course/User.hs @@ -4,7 +4,6 @@ module Handler.Course.User import Import -import Utils.Lens import Utils.Form import Handler.Utils import Database.Esqueleto.Utils.TH diff --git a/src/Handler/Course/Users.hs b/src/Handler/Course/Users.hs index f0c0da708..0549a0745 100644 --- a/src/Handler/Course/Users.hs +++ b/src/Handler/Course/Users.hs @@ -9,7 +9,6 @@ module Handler.Course.Users import Import -import Utils.Lens import Utils.Form import Handler.Utils import Handler.Utils.Database diff --git a/src/Handler/CryptoIDDispatch.hs b/src/Handler/CryptoIDDispatch.hs index 54c2ec760..8a34cde8d 100644 --- a/src/Handler/CryptoIDDispatch.hs +++ b/src/Handler/CryptoIDDispatch.hs @@ -5,8 +5,6 @@ module Handler.CryptoIDDispatch import Import -import Data.Proxy - import qualified Data.Text as Text import Yesod.Core.Types (HandlerContents(..), ErrorResponse(..)) diff --git a/src/Handler/Exam/AddUser.hs b/src/Handler/Exam/AddUser.hs index f8e250831..7aafb58e2 100644 --- a/src/Handler/Exam/AddUser.hs +++ b/src/Handler/Exam/AddUser.hs @@ -8,8 +8,6 @@ import Handler.Exam.RegistrationInvite import Handler.Utils import Handler.Utils.Exam import Handler.Utils.Invitations - -import Utils.Lens import qualified Data.Set as Set diff --git a/src/Handler/Exam/CorrectorInvite.hs b/src/Handler/Exam/CorrectorInvite.hs index cc2882679..f8398487a 100644 --- a/src/Handler/Exam/CorrectorInvite.hs +++ b/src/Handler/Exam/CorrectorInvite.hs @@ -12,8 +12,6 @@ import Import import Handler.Utils.Invitations import Handler.Utils.Exam -import Utils.Lens - import Text.Hamlet (ihamlet) import Data.Aeson hiding (Result(..)) diff --git a/src/Handler/Exam/Edit.hs b/src/Handler/Exam/Edit.hs index 06abd7834..99bd12772 100644 --- a/src/Handler/Exam/Edit.hs +++ b/src/Handler/Exam/Edit.hs @@ -6,8 +6,6 @@ import Import import Handler.Exam.Form import Handler.Exam.CorrectorInvite -import Utils.Lens - import qualified Data.Set as Set import Handler.Utils diff --git a/src/Handler/Exam/Form.hs b/src/Handler/Exam/Form.hs index 1020c6f28..b2c51c946 100644 --- a/src/Handler/Exam/Form.hs +++ b/src/Handler/Exam/Form.hs @@ -8,8 +8,6 @@ module Handler.Exam.Form ) where import Import -import Utils.Lens hiding (parts) - import Handler.Exam.CorrectorInvite import Handler.Utils @@ -230,12 +228,12 @@ examPartsForm prev = wFormToAForm $ do examFormTemplate :: Entity Exam -> DB ExamForm examFormTemplate (Entity eId Exam{..}) = do - parts <- selectList [ ExamPartExam ==. eId ] [] + examParts <- selectList [ ExamPartExam ==. eId ] [] occurrences <- selectList [ ExamOccurrenceExam ==. eId ] [] correctors <- selectList [ ExamCorrectorExam ==. eId ] [] invitations <- map (\(email, InvDBDataExamCorrector) -> email) <$> sourceInvitationsList eId - parts' <- forM parts $ \(Entity pid part) -> (,) <$> encrypt pid <*> pure part + examParts' <- forM examParts $ \(Entity pid part) -> (,) <$> encrypt pid <*> pure part occurrences' <- forM occurrences $ \(Entity oid occ) -> (,) <$> encrypt oid <*> pure occ return ExamForm @@ -267,7 +265,7 @@ examFormTemplate (Entity eId Exam{..}) = do , eofDescription = examOccurrenceDescription } , efExamParts = Set.fromList $ do - (Just -> epfId, ExamPart{..}) <- parts' + (Just -> epfId, ExamPart{..}) <- examParts' return ExamPartForm { epfId , epfName = examPartName diff --git a/src/Handler/Exam/RegistrationInvite.hs b/src/Handler/Exam/RegistrationInvite.hs index 6ebcae157..2b41622b9 100644 --- a/src/Handler/Exam/RegistrationInvite.hs +++ b/src/Handler/Exam/RegistrationInvite.hs @@ -16,8 +16,6 @@ import Handler.Utils.Invitations import qualified Data.Set as Set import Text.Hamlet (ihamlet) - -import Utils.Lens import Data.Aeson hiding (Result(..)) diff --git a/src/Handler/Exam/Show.hs b/src/Handler/Exam/Show.hs index ad371d147..72c6058b4 100644 --- a/src/Handler/Exam/Show.hs +++ b/src/Handler/Exam/Show.hs @@ -5,8 +5,6 @@ module Handler.Exam.Show import Import import Handler.Exam.Register -import Utils.Lens hiding (parts) - import Data.Map ((!?)) import qualified Data.Map as Map @@ -24,7 +22,7 @@ getEShowR tid ssh csh examn = do cTime <- liftIO getCurrentTime mUid <- maybeAuthId - (Entity _ Exam{..}, parts, examVisible, (gradingVisible, gradingShown), (occurrenceAssignmentsVisible, occurrenceAssignmentsShown), results, result, occurrences, (registered, mayRegister), occurrenceNamesShown) <- runDB $ do + (Entity _ Exam{..}, examParts, examVisible, (gradingVisible, gradingShown), (occurrenceAssignmentsVisible, occurrenceAssignmentsShown), results, result, occurrences, (registered, mayRegister), occurrenceNamesShown) <- runDB $ do exam@(Entity eId Exam{..}) <- fetchExam tid ssh csh examn let examVisible = NTop (Just cTime) >= NTop examVisibleFrom @@ -35,12 +33,12 @@ getEShowR tid ssh csh examn = do let occurrenceAssignmentsVisible = NTop (Just cTime) >= NTop examPublishOccurrenceAssignments occurrenceAssignmentsShown <- or2M (return occurrenceAssignmentsVisible) . hasReadAccessTo $ CExamR tid ssh csh examn EEditR - parts <- selectList [ ExamPartExam ==. eId ] [ Asc ExamPartName ] + examParts <- selectList [ ExamPartExam ==. eId ] [ Asc ExamPartName ] resultsRaw <- for mUid $ \uid -> E.select . E.from $ \examPartResult -> do E.where_ $ examPartResult E.^. ExamPartResultUser E.==. E.val uid - E.&&. examPartResult E.^. ExamPartResultExamPart `E.in_` E.valList (map entityKey parts) + E.&&. examPartResult E.^. ExamPartResultExamPart `E.in_` E.valList (map entityKey examParts) return examPartResult let results = maybe Map.empty (\rs -> Map.fromList [ (examPartResultExamPart, res) | res@(Entity _ ExamPartResult{..}) <- rs ]) resultsRaw @@ -66,7 +64,7 @@ getEShowR tid ssh csh examn = do occurrenceNamesShown <- hasReadAccessTo $ CExamR tid ssh csh examn EEditR - return (exam, parts, examVisible, (gradingVisible, gradingShown), (occurrenceAssignmentsVisible, occurrenceAssignmentsShown), results, result, occurrences, (registered, mayRegister), occurrenceNamesShown) + return (exam, examParts, examVisible, (gradingVisible, gradingShown), (occurrenceAssignmentsVisible, occurrenceAssignmentsShown), results, result, occurrences, (registered, mayRegister), occurrenceNamesShown) let examTimes = all (\(Entity _ ExamOccurrence{..}, _) -> Just examOccurrenceStart == examStart && examOccurrenceEnd == examEnd) occurrences registerWidget diff --git a/src/Handler/Exam/Users.hs b/src/Handler/Exam/Users.hs index 35a8842a4..692a69c3c 100644 --- a/src/Handler/Exam/Users.hs +++ b/src/Handler/Exam/Users.hs @@ -6,7 +6,6 @@ module Handler.Exam.Users import Import -import Utils.Lens import Handler.Utils import Handler.Utils.Exam import Handler.Utils.Table.Columns @@ -16,22 +15,21 @@ import Handler.Utils.Csv import qualified Database.Esqueleto as E import qualified Database.Esqueleto.Utils as E import Database.Esqueleto.Utils.TH - + import qualified Data.Csv as Csv - + import Data.Map ((!)) import qualified Data.Map as Map import qualified Data.Set as Set - + import qualified Data.Text as Text import qualified Data.Text.Lens as Text - + import qualified Data.Conduit.List as C - + import qualified Data.CaseInsensitive as CI import Numeric.Lens (integral) -import Control.Arrow (Kleisli(..)) import Database.Persist.Sql (deleteWhereCount, updateWhereCount) @@ -109,7 +107,7 @@ data ExamUserTableCsv = ExamUserTableCsv , csvEUserExerciseNumPasses :: Maybe Int , csvEUserExercisePointsMax :: Maybe Points , csvEUserExerciseNumPassesMax :: Maybe Int - , csvEUserExamResult :: Maybe (Either ExamResultPassed ExamResultGrade) + , csvEUserExamResult :: Maybe ExamResultPassedGrade , csvEUserCourseNote :: Maybe Html } deriving (Generic) @@ -124,23 +122,20 @@ instance ToNamedRecord ExamUserTableCsv where instance FromNamedRecord ExamUserTableCsv where parseNamedRecord csv -- Manually defined awaiting issue #427 = ExamUserTableCsv - <$> csv .:? "surname" - <*> csv .:? "first-name" - <*> csv .:? "name" - <*> csv .:? "matriculation" - <*> csv .:? "field" - <*> csv .:? "degree" - <*> csv .:? "semester" - <*> csv .:? "occurrence" - <*> csv .:? "exercise-points" - <*> csv .:? "exercise-num-passes" - <*> csv .:? "exercise-points-max" - <*> csv .:? "exercise-num-passes-max" - <*> csv .:? "exam-result" - <*> csv .:? "course-note" - where - (.:?) :: FromField (Maybe a) => Csv.NamedRecord -> ByteString -> Csv.Parser (Maybe a) - m .:? name = Csv.lookup m name <|> return Nothing + <$> csv .:?? "surname" + <*> csv .:?? "first-name" + <*> csv .:?? "name" + <*> csv .:?? "matriculation" + <*> csv .:?? "field" + <*> csv .:?? "degree" + <*> csv .:?? "semester" + <*> csv .:?? "occurrence" + <*> csv .:?? "exercise-points" + <*> csv .:?? "exercise-num-passes" + <*> csv .:?? "exercise-points-max" + <*> csv .:?? "exercise-num-passes-max" + <*> csv .:?? "exam-result" + <*> csv .:?? "course-note" instance DefaultOrdered ExamUserTableCsv where headerOrder = Csv.genericHeaderOrder examUserTableCsvOptions @@ -209,7 +204,7 @@ data ExamUserCsvAction } | ExamUserCsvSetResultData { examUserCsvActUser :: UserId - , examUserCsvActExamResult :: Maybe (Either ExamResultPassed ExamResultGrade) + , examUserCsvActExamResult :: Maybe ExamResultPassedGrade } | ExamUserCsvSetCourseNoteData { examUserCsvActUser :: UserId @@ -244,8 +239,8 @@ postEUsersR tid ssh csh examn = do showPasses = numSheetsPasses allBoni /= 0 showPoints = getSum (numSheetsPoints allBoni) /= 0 - resultView :: ExamResultGrade -> Either ExamResultPassed ExamResultGrade - resultView = bool (Left . over _examResult (view passingGrade)) Right examShowGrades + resultView :: ExamResultGrade -> ExamResultPassedGrade + resultView = fmap $ bool (Left . view passingGrade) Right examShowGrades let examUsersDBTable = DBTable{..} @@ -320,7 +315,7 @@ postEUsersR tid ssh csh examn = do criteria'' | ExamAttended (ExamPassed True) `Set.member` criteria = criteria' `Set.union` Set.fromList passed - | otherwise + | otherwise = criteria' in queryExamResult row E.?. ExamResultResult `E.in_` E.valList (Just <$> Set.toList criteria'') ) @@ -431,7 +426,7 @@ postEUsersR tid ssh csh examn = do ExamUserCsvCourseRegister -> DBCsvActionNew ExamUserCsvRegister -> DBCsvActionNew ExamUserCsvDeregister -> DBCsvActionMissing - _other -> DBCsvActionExisting + _other -> DBCsvActionExisting , dbtCsvExecuteActions = do C.mapM_ $ \case ExamUserCsvCourseRegisterData{..} -> do @@ -471,7 +466,7 @@ postEUsersR tid ssh csh examn = do deleteBy $ UniqueExamResult eid examUserCsvActUser audit $ TransactionExamResultDeleted eid examUserCsvActUser Just res -> do - let res' = either (over _examResult $ review passingGrade) id res + let res' = either (review passingGrade) id <$> res now <- liftIO getCurrentTime void $ upsertBy (UniqueExamResult eid examUserCsvActUser) @@ -496,7 +491,7 @@ postEUsersR tid ssh csh examn = do ExamUserCsvSetCourseNoteData{ examUserCsvActCourseNote = Just note, .. } -> do now <- liftIO getCurrentTime uid <- liftHandlerT requireAuthId - Entity nid _ <- upsert (CourseUserNote examCourse examUserCsvActUser note) [ CourseUserNoteNote =. note ] + Entity nid _ <- upsert (CourseUserNote examCourse examUserCsvActUser note) [ CourseUserNoteNote =. note ] insert_ $ CourseUserNoteEdit uid now nid return $ CExamR tid ssh csh examn EUsersR , dbtCsvRenderKey = \(registeredUserName -> registeredUserName') -> \case @@ -550,11 +545,7 @@ postEUsersR tid ssh csh examn = do $newline never ^{nameWidget userDisplayName userSurname} $maybe newResult <- examUserCsvActExamResult - $case newResult - $of Left pResult - , _{pResult} - $of Right gResult - , _{gResult} + , _{newResult} $nothing , _{MsgExamResultNone} |] @@ -572,19 +563,11 @@ postEUsersR tid ssh csh examn = do , dbtCsvRenderException = ap getMessageRender . pure :: ExamUserCsvException -> DB Text } where - studyFeaturesWidget :: StudyFeaturesId -> Widget - studyFeaturesWidget featId = do - (StudyFeatures{studyFeaturesSemester}, (degree, terms)) <- liftHandlerT . runDB . ($ featId) . runKleisli $ Kleisli getJust >>> Kleisli return &&& Kleisli (getJust . studyFeaturesDegree) &&& Kleisli (getJust . studyFeaturesField) - [whamlet| - $newline never - _{StudyDegreeTerm degree terms}, _{MsgStudyFeatureAge} #{studyFeaturesSemester} - |] - registeredUserName :: Map (E.Value ExamRegistrationId) ExamUserTableData -> ExamRegistrationId -> Widget registeredUserName existing (E.Value -> registration) = nameWidget userDisplayName userSurname where Entity _ User{..} = view resultUser $ existing ! registration - + guessUser :: ExamUserTableCsv -> DB (Bool, UserId) guessUser ExamUserTableCsv{..} = $cachedHereBinary (csvEUserMatriculation, csvEUserName, csvEUserSurname) $ do users <- E.select . E.from $ \user -> do @@ -617,30 +600,39 @@ postEUsersR tid ssh csh examn = do lookupStudyFeatures :: ExamUserTableCsv -> DB (Maybe StudyFeaturesId) lookupStudyFeatures csv@ExamUserTableCsv{..} = do uid <- view _2 <$> guessUser csv - studyFeatures <- E.select . E.from $ \(studyFeatures `E.InnerJoin` studyDegree `E.InnerJoin` studyTerms) -> do - E.on $ studyTerms E.^. StudyTermsId E.==. studyFeatures E.^. StudyFeaturesField - E.on $ studyDegree E.^. StudyDegreeId E.==. studyFeatures E.^. StudyFeaturesDegree - E.where_ . E.and $ catMaybes - [ do - field <- csvEUserField - return . E.or $ catMaybes - [ Just $ studyTerms E.^. StudyTermsName `E.ciEq` E.just (E.val field) - , Just $ studyTerms E.^. StudyTermsShorthand `E.ciEq` E.just (E.val field) - , (studyTerms E.^. StudyTermsKey E.==.) . E.val <$> readMay field - ] - , do - degree <- csvEUserDegree - return . E.or $ catMaybes - [ Just $ studyDegree E.^. StudyDegreeName `E.ciEq` E.just (E.val degree) - , Just $ studyDegree E.^. StudyDegreeShorthand `E.ciEq` E.just (E.val degree) - , (studyDegree E.^. StudyDegreeKey E.==.) . E.val <$> readMay degree - ] - , (studyFeatures E.^. StudyFeaturesSemester E.==.) . E.val <$> csvEUserSemester - ] - E.where_ $ studyFeatures E.^. StudyFeaturesUser E.==. E.val uid - E.&&. studyFeatures E.^. StudyFeaturesValid E.==. E.val True - E.limit 2 - return $ studyFeatures E.^. StudyFeaturesId + oldFeatures <- getBy $ UniqueParticipant uid examCourse + studyFeatures <- E.select . E.from $ \(studyFeatures `E.InnerJoin` studyDegree `E.InnerJoin` studyTerms) -> + E.distinctOnOrderBy [ E.asc (studyFeatures E.^. StudyFeaturesField) + , E.asc (studyFeatures E.^. StudyFeaturesDegree) + , E.asc (studyFeatures E.^. StudyFeaturesSemester)] $ do + E.on $ studyTerms E.^. StudyTermsId E.==. studyFeatures E.^. StudyFeaturesField + E.on $ studyDegree E.^. StudyDegreeId E.==. studyFeatures E.^. StudyFeaturesDegree + E.where_ . E.and $ catMaybes + [ do + field <- csvEUserField + return . E.or $ catMaybes + [ Just $ studyTerms E.^. StudyTermsName `E.ciEq` E.just (E.val field) + , Just $ studyTerms E.^. StudyTermsShorthand `E.ciEq` E.just (E.val field) + , (studyTerms E.^. StudyTermsKey E.==.) . E.val <$> readMay field + ] + , do + degree <- csvEUserDegree + return . E.or $ catMaybes + [ Just $ studyDegree E.^. StudyDegreeName `E.ciEq` E.just (E.val degree) + , Just $ studyDegree E.^. StudyDegreeShorthand `E.ciEq` E.just (E.val degree) + , (studyDegree E.^. StudyDegreeKey E.==.) . E.val <$> readMay degree + ] + , (studyFeatures E.^. StudyFeaturesSemester E.==.) . E.val <$> csvEUserSemester + ] + E.where_ $ studyFeatures E.^. StudyFeaturesUser E.==. E.val uid + let isActive = studyFeatures E.^. StudyFeaturesValid E.==. E.val True + isActiveOrPrevious = case oldFeatures of + Just (Entity _ CourseParticipant{courseParticipantField = Just sfid}) + -> isActive E.||. (E.val sfid E.==. studyFeatures E.^. StudyFeaturesId) + _ -> isActive + E.where_ isActiveOrPrevious -- either active studyFeature or the one previously associated with this course + E.orderBy [E.desc isActiveOrPrevious, E.asc (E.orderByOrd $ studyFeatures E.^. StudyFeaturesType)] + return $ studyFeatures E.^. StudyFeaturesId case studyFeatures of [E.Value fid] -> return $ Just fid _other @@ -648,6 +640,11 @@ postEUsersR tid ssh csh examn = do , is _Nothing csvEUserDegree , is _Nothing csvEUserSemester -> return Nothing + _other + | Just (Entity _ CourseParticipant{..}) <- oldFeatures + , Just sfid <- courseParticipantField + , E.Value sfid `elem` studyFeatures + -> return $ Just sfid _other -> throwM ExamUserCsvExceptionNoMatchingStudyFeatures examUsersDBTableValidator = def & defaultSorting [SortAscBy "user-name"] diff --git a/src/Handler/Health.hs b/src/Handler/Health.hs index 36649a436..dad7ef747 100644 --- a/src/Handler/Health.hs +++ b/src/Handler/Health.hs @@ -5,8 +5,6 @@ import Import import qualified Data.Aeson.Encode.Pretty as Aeson import qualified Data.Text.Lazy.Builder as Builder -import Utils.Lens - import qualified Data.UUID as UUID import Data.Semigroup (Min(..), Max(..)) diff --git a/src/Handler/Home.hs b/src/Handler/Home.hs index 51e9975a5..30beb116a 100644 --- a/src/Handler/Home.hs +++ b/src/Handler/Home.hs @@ -2,7 +2,6 @@ module Handler.Home where import Import -import Utils.Lens import Handler.Utils import Handler.Utils.Table.Cells @@ -15,142 +14,16 @@ import qualified Database.Esqueleto.Utils as E getHomeR :: Handler Html getHomeR = do muid <- maybeAuthId - upcomingExamsWidget <- for muid $ runDB . homeUpcomingExams defaultLayout $ do setTitleI MsgHomeHeading - fromMaybe mempty upcomingExamsWidget - maybe mempty homeUpcomingSheets muid - homeOpenAllocations - homeOpenCourses + case muid of + Just uid -> do + homeUpcomingExams uid + homeUpcomingSheets uid + Nothing -> + $(i18nWidgetFile "unauth-home") -homeOpenAllocations :: Widget -homeOpenAllocations = do - cTime <- liftIO getCurrentTime - let tableData :: E.SqlExpr (Entity Allocation) - -> E.SqlQuery (E.SqlExpr (Entity Allocation)) - tableData allocation = do - E.where_ $ E.maybe E.false (\rf -> rf E.<=. E.val cTime) (allocation E.^. AllocationRegisterFrom) - E.&&. E.maybe E.true (\rt -> rt E.>=. E.val cTime) (allocation E.^. AllocationRegisterTo) - return allocation - - colonnade :: Colonnade Sortable (DBRow (Entity Allocation)) (DBCell (HandlerT UniWorX IO) ()) - colonnade = mconcat - [ -- dbRow - sortable (Just "term") (i18nCell MsgTerm) - $ \DBRow{ dbrOutput=Entity{entityVal = Allocation{..}} } -> - anchorCell (TermCourseListR allocationTerm) [whamlet|#{allocationTerm}|] - , sortable (Just "schoolshort") (i18nCell MsgAllocationSchoolShort) - $ \DBRow{ dbrOutput=(Entity _ Allocation{..}) } -> - anchorCell (TermSchoolCourseListR allocationTerm allocationSchool) [whamlet|_{unSchoolKey allocationSchool}|] - , sortable (Just "allocation") (i18nCell MsgAllocation) $ \DBRow{ dbrOutput=Entity{entityVal = Allocation{..}} } -> do - anchorCell (AllocationR allocationTerm allocationSchool allocationShorthand AShowR) allocationName - , sortable (Just "deadline") (i18nCell MsgAllocationRegisterTo) $ \DBRow{ dbrOutput=Entity{entityVal = Allocation{..}} } -> - cell $ traverse (formatTime SelFormatDateTime) allocationRegisterTo >>= maybe mempty toWidget - ] - validator = def & defaultSorting [SortAscBy "deadline", SortAscBy "allocation"] - allocationTable <- liftHandlerT . runDB $ dbTableWidget' validator DBTable - { dbtSQLQuery = tableData - , dbtRowKey = (E.^. AllocationId) - , dbtColonnade = colonnade - , dbtProj = return - , dbtSorting = Map.fromList - [ ( "term" - , SortColumn $ \allocation -> allocation E.^. AllocationTerm - ) - , ( "schoolshort" - , SortColumn $ \allocation -> allocation E.^. AllocationSchool - ) - , ( "allocation" - , SortColumn $ \allocation -> allocation E.^. AllocationShorthand - ) - , ( "deadline" - , SortColumn $ \allocation -> allocation E.^. AllocationRegisterTo - ) - ] - , dbtFilter = mempty - , dbtFilterUI = mempty - , dbtStyle = def - , dbtParams = def - , dbtIdent = "open-allocations" :: Text - , dbtCsvEncode = noCsvEncode - , dbtCsvDecode = Nothing - } - $(widgetFile "home/openAllocations") - - -homeOpenCourses :: Widget -homeOpenCourses = do - cTime <- liftIO getCurrentTime - let tableData :: E.SqlExpr (Entity Course) - -> E.SqlQuery (E.SqlExpr (Entity Course)) - tableData course = do - E.where_ $ E.not_ (E.isNothing $ course E.^. CourseRegisterFrom) -- DO: do this with isAuthorized in dbtProj - E.&&. (course E.^. CourseRegisterFrom E.<=. E.val (Just cTime)) - E.&&. ( E.isNothing (course E.^. CourseRegisterTo) - E.||. course E.^. CourseRegisterTo E.>=. E.val (Just cTime) - ) - E.&&. E.not_ (E.exists . E.from $ \(allocation `E.InnerJoin` allocationCourse) -> do - E.on $ allocation E.^. AllocationId E.==. allocationCourse E.^. AllocationCourseAllocation - E.where_ $ allocationCourse E.^. AllocationCourseCourse E.==. course E.^. CourseId - E.where_ $ E.maybe E.true (\rf -> rf E.>. E.val cTime) (allocation E.^. AllocationRegisterFrom) - E.||. E.maybe E.false (\rt -> rt E.<. E.val cTime) (allocation E.^. AllocationRegisterTo) - E.where_ $ E.maybe E.true (\rf -> rf E.>. E.val cTime) (allocation E.^. AllocationRegisterByCourse) - ) - return course - - colonnade :: Colonnade Sortable (DBRow (Entity Course)) (DBCell (HandlerT UniWorX IO) ()) - colonnade = mconcat - [ -- dbRow - sortable (Just "term") (i18nCell MsgTerm) - $ \DBRow{ dbrOutput=Entity{entityVal = Course{..}} } -> - anchorCell (TermCourseListR courseTerm) [whamlet|#{courseTerm}|] - , sortable (Just "schoolshort") (i18nCell MsgCourseSchoolShort) - $ \DBRow{ dbrOutput=(Entity _ Course{..}) } -> - anchorCell (TermSchoolCourseListR courseTerm courseSchool) [whamlet|_{unSchoolKey courseSchool}|] - , sortable (Just "course") (i18nCell MsgCourse) $ \DBRow{ dbrOutput=Entity{entityVal = course} } -> do - let tid = courseTerm course - ssh = courseSchool course - csh = courseShorthand course - anchorCell (CourseR tid ssh csh CShowR) csh - , sortable (Just "deadline") (i18nCell MsgRegisterTo) $ \DBRow{ dbrOutput=Entity{entityVal = course} } -> - cell $ traverse (formatTime SelFormatDateTime) (courseRegisterTo course) >>= maybe mempty toWidget - ] - validator = def & defaultSorting [SortAscBy "deadline", SortAscBy "course"] - courseTable <- liftHandlerT . runDB $ dbTableWidget' validator DBTable - { dbtSQLQuery = tableData - , dbtRowKey = (E.^. CourseId) - , dbtColonnade = colonnade - , dbtProj = return - , dbtSorting = Map.fromList - [ ( "term" - , SortColumn $ \course -> course E.^. CourseTerm - ) - , ( "schoolshort" - , SortColumn $ \course -> course E.^. CourseSchool - ) - , ( "course" - , SortColumn $ \course -> course E.^. CourseShorthand - ) - , ( "deadline" - , SortColumn $ \course -> course E.^. CourseRegisterTo - ) - ] - , dbtFilter = mempty {- [ ( "term" - , FilterColumn $ \(course `E.InnerJoin` _ `E.InnerJoin` _ ) tids -> if - | Set.null tids -> E.val True :: E.SqlExpr (E.Value Bool) - | otherwise -> course E.^. CourseTerm `E.in_` E.valList (Set.toList tids) - ) - ] -} - , dbtFilterUI = mempty - , dbtStyle = def - , dbtParams = def - , dbtIdent = "open-courses" :: Text - , dbtCsvEncode = noCsvEncode - , dbtCsvDecode = Nothing - } - $(widgetFile "home/openCourses") - homeUpcomingSheets :: UserId -> Widget homeUpcomingSheets uid = do cTime <- liftIO getCurrentTime @@ -253,134 +126,137 @@ homeUpcomingSheets uid = do $(widgetFile "home/upcomingSheets") - -homeUpcomingExams :: UserId -> DB Widget +homeUpcomingExams :: UserId -> Widget homeUpcomingExams uid = do now <- liftIO getCurrentTime - let fortnight = addWeeks 2 now - let -- code copied and slightly adapted from Handler.Course.getCShowR: - examDBTable = DBTable{..} - where - -- for ease of refactoring: - queryCourse = $(sqlIJproj 2 1) . $(sqlLOJproj 3 1) - queryExam = $(sqlIJproj 2 2) . $(sqlLOJproj 3 1) - lensCourse = _1 - lensExam = _2 - lensRegister = _3 . _Just - lensOccurrence = _4 . _Just + ((Any hasExams, examTable), warningDays) <- liftHandlerT . runDB $ do + User {userWarningDays} <- get404 uid + let fortnight = addUTCTime userWarningDays now + let -- code copied and slightly adapted from Handler.Course.getCShowR: + examDBTable = DBTable{..} + where + -- for ease of refactoring: + queryCourse = $(sqlIJproj 2 1) . $(sqlLOJproj 3 1) + queryExam = $(sqlIJproj 2 2) . $(sqlLOJproj 3 1) + lensCourse = _1 + lensExam = _2 + lensRegister = _3 . _Just + lensOccurrence = _4 . _Just - dbtSQLQuery ((course `E.InnerJoin` exam) `E.LeftOuterJoin` register `E.LeftOuterJoin` occurrence) = do - E.on $ register E.?. ExamRegistrationOccurrence E.==. E.just (occurrence E.?. ExamOccurrenceId) - E.on $ register E.?. ExamRegistrationExam E.==. E.just (exam E.^. ExamId) - E.&&. register E.?. ExamRegistrationUser E.==. E.just (E.val uid) - E.on $ course E.^. CourseId E.==. exam E.^. ExamCourse - E.where_ $ E.exists $ E.from $ \participant -> - E.where_ $ participant E.^. CourseParticipantUser E.==. E.val uid - E.&&. participant E.^. CourseParticipantCourse E.==. course E.^. CourseId - let regToWithinFortnight = exam E.^. ExamRegisterTo E.<=. E.just (E.val fortnight) - E.&&. exam E.^. ExamRegisterTo E.>=. E.just (E.val now) - E.&&. E.isNothing (register E.?. ExamRegistrationId) - startExamFortnight = exam E.^. ExamStart E.<=. E.just (E.val fortnight) - E.&&. exam E.^. ExamStart E.>=. E.just (E.val now) - E.&&. E.isJust (register E.?. ExamRegistrationId) - startOccurFortnight = occurrence E.?. ExamOccurrenceStart E.<=. E.just (E.val fortnight) - E.&&. occurrence E.?. ExamOccurrenceStart E.>=. E.just (E.val now) - E.&&. E.isJust (register E.?. ExamRegistrationId) - earliestOccurrence = E.sub_select $ E.from $ \occ -> do - E.where_ $ occ E.^. ExamOccurrenceExam E.==. exam E.^. ExamId - E.&&. occ E.^. ExamOccurrenceStart E.>=. E.val now - return $ E.min_ $ occ E.^. ExamOccurrenceStart - startEarliest = E.isNothing (occurrence E.?. ExamOccurrenceId) - E.&&. earliestOccurrence E.<=. E.just (E.val fortnight) - -- E.&&. earliestOccurrence E.>=. E.just (E.val now) - E.where_ $ regToWithinFortnight E.||. startExamFortnight E.||. startOccurFortnight E.||. startEarliest - return (course, exam, register, occurrence) - dbtRowKey = queryExam >>> (E.^. ExamId) - dbtProj r@DBRow{ dbrOutput } = do - let Entity _ Exam{..} = view lensExam dbrOutput - Entity _ Course{..} = view lensCourse dbrOutput - guardM . hasReadAccessTo $ CExamR courseTerm courseSchool courseShorthand examName EShowR -- check access rights - return r - dbtColonnade = dbColonnade $ mconcat - [ sortable (Just "term") (i18nCell MsgTerm) $ \DBRow{ dbrOutput = view lensCourse -> Entity _ Course{..} } -> - msgCell courseTerm - , sortable (Just "school") (i18nCell MsgCourseSchool) $ \DBRow{ dbrOutput = view lensCourse -> Entity _ Course{..} } -> - msgCell courseSchool - , sortable (Just "course") (i18nCell MsgCourse) $ \DBRow{ dbrOutput = view lensCourse -> Entity _ Course{..} } -> - anchorCell (CourseR courseTerm courseSchool courseShorthand CShowR) (toWgt courseShorthand) - -- continue here - , sortable (Just "name") (i18nCell MsgExamName) $ \DBRow{ dbrOutput } -> do - let Entity _ Exam{..} = view lensExam dbrOutput - Entity _ Course{..} = view lensCourse dbrOutput - indicatorCell <> anchorCell (CExamR courseTerm courseSchool courseShorthand examName EShowR) examName - , sortable (Just "register-from") (i18nCell MsgExamRegisterFrom) $ \DBRow { dbrOutput = view lensExam -> Entity _ Exam{..} } -> maybe mempty dateTimeCell examRegisterFrom - , sortable (Just "register-to") (i18nCell MsgExamRegisterTo) $ \DBRow { dbrOutput = view lensExam -> Entity _ Exam{..} } -> maybe mempty dateTimeCell examRegisterTo - , sortable (Just "time") (i18nCell MsgExamTime) $ \DBRow{ dbrOutput } -> - if | Just (Entity _ ExamOccurrence{..}) <- preview lensOccurrence dbrOutput - -> cell $ formatTimeRangeW SelFormatDateTime examOccurrenceStart examOccurrenceEnd - | Entity _ Exam{..} <- view lensExam dbrOutput - , Just start <- examStart -> cell $ formatTimeRangeW SelFormatDateTime start examEnd - | otherwise -> mempty - {- NOTE: We do not want thoughtless exam registrations, since many people click "register" and don't show up, causing logistic problems. - Hence we force them here to click twice. Maybe add a captcha where users have to distinguish pictures showing pink elephants and course lecturers. - , sortable Nothing mempty $ \DBRow{ dbrOutput } -> sqlCell $ do - let Entity eId Exam{..} = view lensExam dbrOutput - Entity _ Course{..} = view lensCourse dbrOutput - mayRegister <- (== Authorized) <$> evalAccessDB (CExamR courseTerm courseSchool courseShorthand examName ERegisterR) True - isRegistered <- existsBy $ UniqueExamRegistration eId uid - if - | mayRegister -> do - (examRegisterForm, examRegisterEnctype) <- liftHandlerT . generateFormPost . buttonForm' $ bool [BtnRegister] [BtnDeregister] isRegistered - return $ wrapForm examRegisterForm def - { formAction = Just . SomeRoute $ CExamR courseTerm courseSchool courseShorthand examName ERegisterR - , formEncoding = examRegisterEnctype - , formSubmit = FormNoSubmit - } - | isRegistered -> return [whamlet|_{MsgExamRegistered}|] - | otherwise -> return mempty - -} - , sortable (Just "registered") (i18nCell MsgExamRegistration ) $ \DBRow{ dbrOutput } -> sqlCell $ do - let Entity _ Exam{..} = view lensExam dbrOutput - Entity _ Course{..} = view lensCourse dbrOutput - mayRegister <- (== Authorized) <$> evalAccessDB (CExamR courseTerm courseSchool courseShorthand examName ERegisterR) True - let isRegistered = has lensRegister dbrOutput - label = bool MsgExamNotRegistered MsgExamRegistered isRegistered - examUrl = CExamR courseTerm courseSchool courseShorthand examName EShowR - if | mayRegister -> return $ simpleLinkI (SomeMessage label) examUrl - | otherwise -> return [whamlet|_{label}|] - , sortable (toNothingS "occurrence") (i18nCell MsgExamOccurrence) $ \DBRow{ dbrOutput } -> - if | Just (Entity _ ExamOccurrence{..}) <- preview lensOccurrence dbrOutput - -> textCell examOccurrenceRoom - | otherwise -> mempty - ] - dbtSorting = Map.fromList - [ ("demo-both", SortColumn $ queryCourse &&& queryExam >>> (\(_course,exam)-> exam E.^. ExamName)) - , ("term", SortColumn $ queryCourse >>> (E.^. CourseTerm )) - , ("school", SortColumn $ queryCourse >>> (E.^. CourseSchool )) - , ("course", SortColumn $ queryCourse >>> (E.^. CourseShorthand )) - , ("name", SortColumn $ queryExam >>> (E.^. ExamName )) - , ("time", SortColumn $ queryExam >>> (E.^. ExamStart )) - , ("register-from", SortColumn $ queryExam >>> (E.^. ExamRegisterFrom )) - , ("register-to", SortColumn $ queryExam >>> (E.^. ExamRegisterTo )) - , ("visible", SortColumn $ queryExam >>> (E.^. ExamVisibleFrom )) - , ("registered", SortColumn $ queryExam >>> (\exam -> - E.exists $ E.from $ \registration -> do - E.where_ $ registration E.^. ExamRegistrationUser E.==. E.val uid - E.where_ $ registration E.^. ExamRegistrationExam E.==. exam E.^. ExamId - )) - ] - dbtFilter = Map.empty - dbtFilterUI = const mempty - dbtStyle = def - dbtParams = def - dbtIdent :: Text - dbtIdent = "exams" - dbtCsvEncode = noCsvEncode - dbtCsvDecode = Nothing + dbtSQLQuery ((course `E.InnerJoin` exam) `E.LeftOuterJoin` register `E.LeftOuterJoin` occurrence) = do + E.on $ register E.?. ExamRegistrationOccurrence E.==. E.just (occurrence E.?. ExamOccurrenceId) + E.on $ register E.?. ExamRegistrationExam E.==. E.just (exam E.^. ExamId) + E.&&. register E.?. ExamRegistrationUser E.==. E.just (E.val uid) + E.on $ course E.^. CourseId E.==. exam E.^. ExamCourse + E.where_ $ E.exists $ E.from $ \participant -> + E.where_ $ participant E.^. CourseParticipantUser E.==. E.val uid + E.&&. participant E.^. CourseParticipantCourse E.==. course E.^. CourseId + let regToWithinFortnight = exam E.^. ExamRegisterTo E.<=. E.just (E.val fortnight) + E.&&. exam E.^. ExamRegisterTo E.>=. E.just (E.val now) + E.&&. E.isNothing (register E.?. ExamRegistrationId) + startExamFortnight = exam E.^. ExamStart E.<=. E.just (E.val fortnight) + E.&&. exam E.^. ExamStart E.>=. E.just (E.val now) + E.&&. E.isJust (register E.?. ExamRegistrationId) + startOccurFortnight = occurrence E.?. ExamOccurrenceStart E.<=. E.just (E.val fortnight) + E.&&. occurrence E.?. ExamOccurrenceStart E.>=. E.just (E.val now) + E.&&. E.isJust (register E.?. ExamRegistrationId) + earliestOccurrence = E.sub_select $ E.from $ \occ -> do + E.where_ $ occ E.^. ExamOccurrenceExam E.==. exam E.^. ExamId + E.&&. occ E.^. ExamOccurrenceStart E.>=. E.val now + return $ E.min_ $ occ E.^. ExamOccurrenceStart + startEarliest = E.isNothing (occurrence E.?. ExamOccurrenceId) + E.&&. earliestOccurrence E.<=. E.just (E.val fortnight) + -- E.&&. earliestOccurrence E.>=. E.just (E.val now) + E.where_ $ regToWithinFortnight E.||. startExamFortnight E.||. startOccurFortnight E.||. startEarliest + return (course, exam, register, occurrence) + dbtRowKey = queryExam >>> (E.^. ExamId) + dbtProj r@DBRow{ dbrOutput } = do + let Entity _ Exam{..} = view lensExam dbrOutput + Entity _ Course{..} = view lensCourse dbrOutput + guardM . hasReadAccessTo $ CExamR courseTerm courseSchool courseShorthand examName EShowR -- check access rights + return r + dbtColonnade = dbColonnade $ mconcat + [ sortable (Just "term") (i18nCell MsgTerm) $ \DBRow{ dbrOutput = view lensCourse -> Entity _ Course{..} } -> + msgCell courseTerm + , sortable (Just "school") (i18nCell MsgCourseSchool) $ \DBRow{ dbrOutput = view lensCourse -> Entity _ Course{..} } -> + msgCell courseSchool + , sortable (Just "course") (i18nCell MsgCourse) $ \DBRow{ dbrOutput = view lensCourse -> Entity _ Course{..} } -> + anchorCell (CourseR courseTerm courseSchool courseShorthand CShowR) (toWgt courseShorthand) + -- continue here + , sortable (Just "name") (i18nCell MsgExamName) $ \DBRow{ dbrOutput } -> do + let Entity _ Exam{..} = view lensExam dbrOutput + Entity _ Course{..} = view lensCourse dbrOutput + indicatorCell <> anchorCell (CExamR courseTerm courseSchool courseShorthand examName EShowR) examName + , sortable (Just "register-from") (i18nCell MsgExamRegisterFrom) $ \DBRow { dbrOutput = view lensExam -> Entity _ Exam{..} } -> maybe mempty dateTimeCell examRegisterFrom + , sortable (Just "register-to") (i18nCell MsgExamRegisterTo) $ \DBRow { dbrOutput = view lensExam -> Entity _ Exam{..} } -> maybe mempty dateTimeCell examRegisterTo + , sortable (Just "time") (i18nCell MsgExamTime) $ \DBRow{ dbrOutput } -> + if | Just (Entity _ ExamOccurrence{..}) <- preview lensOccurrence dbrOutput + -> cell $ formatTimeRangeW SelFormatDateTime examOccurrenceStart examOccurrenceEnd + | Entity _ Exam{..} <- view lensExam dbrOutput + , Just start <- examStart -> cell $ formatTimeRangeW SelFormatDateTime start examEnd + | otherwise -> mempty + {- NOTE: We do not want thoughtless exam registrations, since many people click "register" and don't show up, causing logistic problems. + Hence we force them here to click twice. Maybe add a captcha where users have to distinguish pictures showing pink elephants and course lecturers. + , sortable Nothing mempty $ \DBRow{ dbrOutput } -> sqlCell $ do + let Entity eId Exam{..} = view lensExam dbrOutput + Entity _ Course{..} = view lensCourse dbrOutput + mayRegister <- (== Authorized) <$> evalAccessDB (CExamR courseTerm courseSchool courseShorthand examName ERegisterR) True + isRegistered <- existsBy $ UniqueExamRegistration eId uid + if + | mayRegister -> do + (examRegisterForm, examRegisterEnctype) <- liftHandlerT . generateFormPost . buttonForm' $ bool [BtnRegister] [BtnDeregister] isRegistered + return $ wrapForm examRegisterForm def + { formAction = Just . SomeRoute $ CExamR courseTerm courseSchool courseShorthand examName ERegisterR + , formEncoding = examRegisterEnctype + , formSubmit = FormNoSubmit + } + | isRegistered -> return [whamlet|_{MsgExamRegistered}|] + | otherwise -> return mempty + -} + , sortable (Just "registered") (i18nCell MsgExamRegistration ) $ \DBRow{ dbrOutput } -> sqlCell $ do + let Entity _ Exam{..} = view lensExam dbrOutput + Entity _ Course{..} = view lensCourse dbrOutput + mayRegister <- (== Authorized) <$> evalAccessDB (CExamR courseTerm courseSchool courseShorthand examName ERegisterR) True + let isRegistered = has lensRegister dbrOutput + label = bool MsgExamNotRegistered MsgExamRegistered isRegistered + examUrl = CExamR courseTerm courseSchool courseShorthand examName EShowR + if | mayRegister -> return $ simpleLinkI (SomeMessage label) examUrl + | otherwise -> return [whamlet|_{label}|] + , sortable (toNothingS "occurrence") (i18nCell MsgExamOccurrence) $ \DBRow{ dbrOutput } -> + if | Just (Entity _ ExamOccurrence{..}) <- preview lensOccurrence dbrOutput + -> textCell examOccurrenceRoom + | otherwise -> mempty + ] + dbtSorting = Map.fromList + [ ("demo-both", SortColumn $ queryCourse &&& queryExam >>> (\(_course,exam)-> exam E.^. ExamName)) + , ("term", SortColumn $ queryCourse >>> (E.^. CourseTerm )) + , ("school", SortColumn $ queryCourse >>> (E.^. CourseSchool )) + , ("course", SortColumn $ queryCourse >>> (E.^. CourseShorthand )) + , ("name", SortColumn $ queryExam >>> (E.^. ExamName )) + , ("time", SortColumn $ queryExam >>> (E.^. ExamStart )) + , ("register-from", SortColumn $ queryExam >>> (E.^. ExamRegisterFrom )) + , ("register-to", SortColumn $ queryExam >>> (E.^. ExamRegisterTo )) + , ("visible", SortColumn $ queryExam >>> (E.^. ExamVisibleFrom )) + , ("registered", SortColumn $ queryExam >>> (\exam -> + E.exists $ E.from $ \registration -> do + E.where_ $ registration E.^. ExamRegistrationUser E.==. E.val uid + E.where_ $ registration 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) <- dbTable examDBTableValidator examDBTable - return $(widgetFile "home/upcomingExams") + examDBTableValidator = def + & defaultSorting [SortAscBy "time"] + + (, userWarningDays) <$> dbTable examDBTableValidator examDBTable + + $(widgetFile "home/upcomingExams") diff --git a/src/Handler/Material.hs b/src/Handler/Material.hs index d0abf6824..025b0c9bc 100644 --- a/src/Handler/Material.hs +++ b/src/Handler/Material.hs @@ -14,7 +14,6 @@ import qualified Data.Conduit.List as C import qualified Database.Esqueleto as E import Database.Esqueleto.Utils.TH -import Utils.Lens import Utils.Form import Handler.Utils import Handler.Utils.Delete diff --git a/src/Handler/Profile.hs b/src/Handler/Profile.hs index e871575dc..11b9728a3 100644 --- a/src/Handler/Profile.hs +++ b/src/Handler/Profile.hs @@ -5,7 +5,6 @@ import Import import Handler.Utils import Handler.Utils.Table.Cells -import Utils.Lens -- import Colonnade hiding (fromMaybe, singleton) -- import Yesod.Colonnade import Data.Monoid (Any(..)) @@ -25,6 +24,7 @@ data SettingsForm = SettingsForm , stgDate :: DateTimeFormat , stgTime :: DateTimeFormat , stgDownloadFiles :: Bool + , stgWarningDays :: NominalDiffTime , stgNotificationSettings :: NotificationSettings } @@ -51,6 +51,9 @@ makeSettingForm template html = do <*> apopt checkBoxField (fslI MsgDownloadFiles & setTooltip MsgDownloadFilesTip ) (stgDownloadFiles <$> template) + <*> areq daysField (fslI MsgWarningDays + & setTooltip MsgWarningDaysTip + ) (stgWarningDays <$> template) <* aformSection MsgFormNotifications <*> notificationForm (stgNotificationSettings <$> template) return (result, widget) -- no validation required here @@ -182,6 +185,7 @@ postProfileR = do , stgTime = userTimeFormat , stgDownloadFiles = userDownloadFiles , stgNotificationSettings = userNotificationSettings + , stgWarningDays = userWarningDays } ((res,formWidget), formEnctype) <- runFormPost . identifyForm ProfileSettings $ makeSettingForm settingsTemplate @@ -193,6 +197,7 @@ postProfileR = do , UserDateFormat =. stgDate , UserTimeFormat =. stgTime , UserDownloadFiles =. stgDownloadFiles + , UserWarningDays =. stgWarningDays , UserNotificationSettings =. stgNotificationSettings ] when (stgMaxFavourties < userMaxFavourites) $ do diff --git a/src/Handler/Sheet.hs b/src/Handler/Sheet.hs index f1bf685b8..5ee6ba68f 100644 --- a/src/Handler/Sheet.hs +++ b/src/Handler/Sheet.hs @@ -49,11 +49,6 @@ import Data.Map (Map, (!)) import Data.Monoid (Any(..)) --- import Control.Lens -import Utils.Lens - ---import qualified Data.Aeson as Aeson - import Control.Monad.Random.Class (MonadRandom(..)) import Utils.Sql @@ -186,7 +181,7 @@ getSheetListR tid ssh csh = do let hasSFT :: (E.Value Bool, E.Value Bool, E.Value Bool, E.Value Bool) -> [SheetFileType] hasSFT (E.Value hasExercise, E.Value hasHint, E.Value hasSolution, E.Value hasMarking) - = [ sft | sft <- [minBound..maxBound] + = [ sft | sft <- universeF , sft /= SheetExercise || hasExercise , sft /= SheetHint || hasHint , sft /= SheetSolution || hasSolution @@ -204,7 +199,7 @@ getSheetListR tid ssh csh = do E.where_ $ sheet E.^. SheetCourse E.==. E.val cid sheetFilter :: SheetName -> DB Bool - sheetFilter sheetName = (== Authorized) <$> evalAccessDB (CSheetR tid ssh csh sheetName SShowR) False + sheetFilter sheetName = hasReadAccessTo $ CSheetR tid ssh csh sheetName SShowR sheetCol = widgetColonnade . mconcat $ [ -- dbRow , @@ -220,9 +215,9 @@ getSheetListR tid ssh csh = do | let existingSFTs = hasSFT existFiles , sft <- [minBound..maxBound] , let link = CSheetR tid ssh csh sheetName $ SZipR sft - , let icn = toWidget $ sheetFile2markup sft + , let icn = toWgt $ sheetFile2markup sft , let icnCell = if sft `elem` existingSFTs - then linkEmptyCell link icn + then linkEitherCell link (icn, [whamlet| |]) else spacerCell ] id & cellAttrs <>~ [("class","list--inline list--space-separated")] , sortable (Just "submission-since") (i18nCell MsgSheetActiveFrom) diff --git a/src/Handler/Submission.hs b/src/Handler/Submission.hs index 0d2268d24..1d14a8d9f 100644 --- a/src/Handler/Submission.hs +++ b/src/Handler/Submission.hs @@ -6,8 +6,6 @@ import Import import Jobs -import Utils.Lens - -- import Yesod.Form.Bootstrap3 import Handler.Utils diff --git a/src/Handler/SystemMessage.hs b/src/Handler/SystemMessage.hs index ae1c7f757..b2c7ef90f 100644 --- a/src/Handler/SystemMessage.hs +++ b/src/Handler/SystemMessage.hs @@ -11,8 +11,6 @@ import qualified Data.List.NonEmpty as NonEmpty import Handler.Utils import Handler.Utils.Table.Cells -import Utils.Lens - import qualified Database.Esqueleto as E -- htmlField' moved to Handler.Utils.Form/Fields diff --git a/src/Handler/Term.hs b/src/Handler/Term.hs index 8d67d8e5c..26cba329a 100644 --- a/src/Handler/Term.hs +++ b/src/Handler/Term.hs @@ -5,8 +5,6 @@ import Handler.Utils import Handler.Utils.Table.Cells import qualified Data.Map as Map -import Utils.Lens - import qualified Database.Esqueleto as E import qualified Data.Set as Set diff --git a/src/Handler/Tutorial.hs b/src/Handler/Tutorial.hs index 5232dad17..ae2c26ea0 100644 --- a/src/Handler/Tutorial.hs +++ b/src/Handler/Tutorial.hs @@ -25,8 +25,6 @@ import qualified Data.CaseInsensitive as CI import qualified Data.Text as Text -import Utils.Lens - import Data.Aeson hiding (Result(..)) import Text.Hamlet (ihamlet) diff --git a/src/Handler/Tutorial/Users.hs b/src/Handler/Tutorial/Users.hs index 3650755d5..34046f452 100644 --- a/src/Handler/Tutorial/Users.hs +++ b/src/Handler/Tutorial/Users.hs @@ -4,7 +4,6 @@ module Handler.Tutorial.Users import Import -import Utils.Lens import Utils.Form -- import Utils.DB import Handler.Utils diff --git a/src/Handler/Users.hs b/src/Handler/Users.hs index 59f5837c9..a8df63296 100644 --- a/src/Handler/Users.hs +++ b/src/Handler/Users.hs @@ -13,8 +13,6 @@ import Handler.Utils.Invitations import qualified Auth.LDAP as Auth -import Utils.Lens - import qualified Data.CaseInsensitive as CI import qualified Data.Set as Set diff --git a/src/Handler/Utils.hs b/src/Handler/Utils.hs index 65e701eed..0d181cbbd 100644 --- a/src/Handler/Utils.hs +++ b/src/Handler/Utils.hs @@ -4,8 +4,6 @@ module Handler.Utils import Import -import Utils.Lens - import qualified Data.Text as T import qualified Data.Text.Encoding as T import Data.Map ((!)) @@ -15,10 +13,6 @@ import Data.CaseInsensitive (original) -- import qualified Data.CaseInsensitive as CI import qualified Data.Conduit.List as Conduit -import Language.Haskell.TH -import Language.Haskell.TH.Syntax (qRunIO) --- import Language.Haskell.TH.Datatype - import Text.Hamlet (shamletFile) import Handler.Utils.DateTime as Handler.Utils @@ -32,12 +26,9 @@ import Handler.Utils.Rating as Handler.Utils hiding (extractRatings) import Handler.Utils.Sheet as Handler.Utils import Handler.Utils.Mail as Handler.Utils import Handler.Utils.ContentDisposition as Handler.Utils +import Handler.Utils.I18n as Handler.Utils -import System.Directory (listDirectory) -import System.FilePath.Posix (takeBaseName, takeFileName) - -import qualified Data.List as List -import qualified Data.List.NonEmpty as NonEmpty +import System.FilePath.Posix (takeFileName) import Control.Monad.Logger @@ -218,36 +209,6 @@ warnTermDays tid timeNames = do forM_ outoflecture $ warnI MsgDayIsOutOfLecture forM_ outoftermdays $ warnI MsgDayIsOutOfTerm --- | Add language dependent template files --- --- For large files which are translated as a whole. --- --- Argument musst be a directory under @/templates@, --- which contains a file for each language, --- eg. @imprint@ for choosing between --- @/templates/imprint/de.hamlet@, @/templates/imprint/de-at.hamlet@, --- and @/templates/imprint/en.hamlet@ --- --- Dependency detection cannot work properly (no `addDependentFile`-equivalent --- for directories) --- @$ stack clean@ is required so new translations show up -i18nWidgetFile :: FilePath -> Q Exp -i18nWidgetFile basename = do - -- Construct list of available translations (@de@, @en@, ...) at compile time - let i18nDirectory = "templates" "i18n" basename - availableFiles <- qRunIO $ listDirectory i18nDirectory - let availableTranslations = sortWith (NTop . flip List.elemIndex (NonEmpty.toList appLanguages)) . List.nub $ pack . takeBaseName <$> availableFiles - availableTranslations' <- maybe (fail $ "‘" <> i18nDirectory <> "’ is empty") return $ NonEmpty.nonEmpty availableTranslations - - -- Dispatch to correct language (depending on user settings via `selectLanguage`) at run time - ws <- newName "ws" -- Name for dispatch function - letE - [ funD ws $ [ clause [litP $ stringL l] (normalB . widgetFile $ "i18n" basename l) [] - | l <- unpack <$> NonEmpty.toList availableTranslations' -- One function definition for every available language - ] ++ [ clause [wildP] (normalB [e| error "selectLanguage returned an invalid translation" |]) [] ] -- Fallback mostly there so compiler does not complain about non-exhaustive pattern match - ] [e|selectLanguage availableTranslations' >>= $(varE ws)|] - - -- | return a value only if the current user ist authorized for a given route guardAuthorizedFor :: ( HandlerSite h ~ UniWorX, MonadHandler h, MonadLogger h @@ -264,3 +225,11 @@ runAppLoggingT app@(appLogger -> (_, loggerTVar)) = flip runLoggingT logFunc f <- messageLoggerSource app <$> readTVarIO loggerTVar f loc src lvl str +studyFeaturesWidget :: StudyFeaturesId -> Widget +studyFeaturesWidget featId = do + (StudyFeatures{studyFeaturesSemester}, (degree, terms)) <- liftHandlerT . runDB . ($ featId) . runKleisli $ Kleisli getJust >>> Kleisli return &&& Kleisli (getJust . studyFeaturesDegree) &&& Kleisli (getJust . studyFeaturesField) + [whamlet| + $newline never + _{StudyDegreeTerm degree terms}, _{MsgStudyFeatureAge} #{studyFeaturesSemester} + |] + diff --git a/src/Handler/Utils/Communication.hs b/src/Handler/Utils/Communication.hs index 7ee1f815a..2e2ef88c8 100644 --- a/src/Handler/Utils/Communication.hs +++ b/src/Handler/Utils/Communication.hs @@ -9,7 +9,6 @@ module Handler.Utils.Communication import Import import Handler.Utils -import Utils.Lens import Jobs.Queue import Control.Monad.Trans.Reader (mapReaderT) diff --git a/src/Handler/Utils/ContentDisposition.hs b/src/Handler/Utils/ContentDisposition.hs index 7be2bd81b..b353d1bb3 100644 --- a/src/Handler/Utils/ContentDisposition.hs +++ b/src/Handler/Utils/ContentDisposition.hs @@ -5,8 +5,6 @@ module Handler.Utils.ContentDisposition import Import -import Utils.Lens - -- | Check whether the user's preference for files is inline-viewing or downloading downloadFiles :: (MonadHandler m, HandlerSite m ~ UniWorX) => m Bool downloadFiles = do diff --git a/src/Handler/Utils/DateTime.hs b/src/Handler/Utils/DateTime.hs index 8bb33a222..56553ce38 100644 --- a/src/Handler/Utils/DateTime.hs +++ b/src/Handler/Utils/DateTime.hs @@ -16,8 +16,6 @@ module Handler.Utils.DateTime import Import -import Utils.Lens - import Data.Time.Zones import qualified Data.Time.Zones as TZ diff --git a/src/Handler/Utils/Delete.hs b/src/Handler/Utils/Delete.hs index 149ab8285..8a268ac2c 100644 --- a/src/Handler/Utils/Delete.hs +++ b/src/Handler/Utils/Delete.hs @@ -17,8 +17,6 @@ module Handler.Utils.Delete import Import import Handler.Utils.Form -import Utils.Lens - import qualified Data.Text as Text import qualified Data.Set as Set diff --git a/src/Handler/Utils/Exam.hs b/src/Handler/Utils/Exam.hs index 3f53325a8..5cdd6fd29 100644 --- a/src/Handler/Utils/Exam.hs +++ b/src/Handler/Utils/Exam.hs @@ -12,8 +12,6 @@ import qualified Database.Esqueleto.Utils as E import qualified Database.Esqueleto.Internal.Sql as E import Database.Esqueleto.Utils.TH -import Utils.Lens - import qualified Data.Conduit.List as C import qualified Data.Map as Map diff --git a/src/Handler/Utils/Form.hs b/src/Handler/Utils/Form.hs index 4db9808a8..b040cf31e 100644 --- a/src/Handler/Utils/Form.hs +++ b/src/Handler/Utils/Form.hs @@ -40,13 +40,9 @@ import Control.Monad.Error.Class (MonadError(..)) import Data.Either (partitionEithers) -import Utils.Lens - import Data.Aeson (eitherDecodeStrict') import Data.Aeson.Text (encodeToLazyText) -import Data.Proxy - import qualified Text.Email.Validate as Email import Yesod.Core.Types (FileInfo(..)) diff --git a/src/Handler/Utils/Form/MassInput.hs b/src/Handler/Utils/Form/MassInput.hs index c8a869514..7e2131ae3 100644 --- a/src/Handler/Utils/Form/MassInput.hs +++ b/src/Handler/Utils/Form/MassInput.hs @@ -17,7 +17,6 @@ module Handler.Utils.Form.MassInput import Import import Utils.Form -import Utils.Lens import Handler.Utils.Form.MassInput.Liveliness import Handler.Utils.Form.MassInput.TH diff --git a/src/Handler/Utils/Form/Occurrences.hs b/src/Handler/Utils/Form/Occurrences.hs index 753868a9e..ced9c3c0b 100644 --- a/src/Handler/Utils/Form/Occurrences.hs +++ b/src/Handler/Utils/Form/Occurrences.hs @@ -10,8 +10,6 @@ import qualified Data.Set as Set import Data.Map ((!)) import qualified Data.Map as Map -import Utils.Lens - data OccurrenceScheduleKind = ScheduleKindWeekly deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic, Typeable) diff --git a/src/Handler/Utils/I18n.hs b/src/Handler/Utils/I18n.hs new file mode 100644 index 000000000..1119191d2 --- /dev/null +++ b/src/Handler/Utils/I18n.hs @@ -0,0 +1,43 @@ +module Handler.Utils.I18n + where + +import Import + +import Language.Haskell.TH +import Language.Haskell.TH.Syntax (qRunIO) + +import qualified Data.List as List +import qualified Data.List.NonEmpty as NonEmpty + +import System.Directory (listDirectory) +import System.FilePath.Posix (takeBaseName) + + +-- | Add language dependent template files +-- +-- For large files which are translated as a whole. +-- +-- Argument musst be a directory under @/templates@, +-- which contains a file for each language, +-- eg. @imprint@ for choosing between +-- @/templates/imprint/de.hamlet@, @/templates/imprint/de-at.hamlet@, +-- and @/templates/imprint/en.hamlet@ +-- +-- Dependency detection cannot work properly (no `addDependentFile`-equivalent +-- for directories) +-- @$ stack clean@ is required so new translations show up +i18nWidgetFile :: FilePath -> Q Exp +i18nWidgetFile basename = do + -- Construct list of available translations (@de@, @en@, ...) at compile time + let i18nDirectory = "templates" "i18n" basename + availableFiles <- qRunIO $ listDirectory i18nDirectory + let availableTranslations = sortWith (NTop . flip List.elemIndex (NonEmpty.toList appLanguages)) . List.nub $ pack . takeBaseName <$> availableFiles + availableTranslations' <- maybe (fail $ "‘" <> i18nDirectory <> "’ is empty") return $ NonEmpty.nonEmpty availableTranslations + + -- Dispatch to correct language (depending on user settings via `selectLanguage`) at run time + ws <- newName "ws" -- Name for dispatch function + letE + [ funD ws $ [ clause [litP $ stringL l] (normalB . widgetFile $ "i18n" basename l) [] + | l <- unpack <$> NonEmpty.toList availableTranslations' -- One function definition for every available language + ] ++ [ clause [wildP] (normalB [e| error "selectLanguage returned an invalid translation" |]) [] ] -- Fallback mostly there so compiler does not complain about non-exhaustive pattern match + ] [e|selectLanguage availableTranslations' >>= $(varE ws)|] \ No newline at end of file diff --git a/src/Handler/Utils/Invitations.hs b/src/Handler/Utils/Invitations.hs index a4ab660d2..94740d96c 100644 --- a/src/Handler/Utils/Invitations.hs +++ b/src/Handler/Utils/Invitations.hs @@ -16,7 +16,6 @@ module Handler.Utils.Invitations ) where import Import -import Utils.Lens import Utils.Form import Jobs.Queue diff --git a/src/Handler/Utils/Mail.hs b/src/Handler/Utils/Mail.hs index 0548d341c..e370676d5 100644 --- a/src/Handler/Utils/Mail.hs +++ b/src/Handler/Utils/Mail.hs @@ -7,8 +7,6 @@ module Handler.Utils.Mail import Import -import Utils.Lens - import qualified Data.CaseInsensitive as CI import qualified Data.ByteString.Lazy as LBS diff --git a/src/Handler/Utils/Rating.hs b/src/Handler/Utils/Rating.hs index 472e49950..5549766dc 100644 --- a/src/Handler/Utils/Rating.hs +++ b/src/Handler/Utils/Rating.hs @@ -39,8 +39,6 @@ import qualified Database.Esqueleto as E import qualified Data.Conduit.List as Conduit -import Utils.Lens - instance HasResolution prec => Pretty (Fixed prec) where pretty = pretty . show diff --git a/src/Handler/Utils/SheetType.hs b/src/Handler/Utils/SheetType.hs index a4cd057b3..494729793 100644 --- a/src/Handler/Utils/SheetType.hs +++ b/src/Handler/Utils/SheetType.hs @@ -5,7 +5,6 @@ module Handler.Utils.SheetType import Import import Data.Monoid (Sum(..)) -import Utils.Lens addBonusToPoints :: SheetTypeSummary -> SheetTypeSummary addBonusToPoints sts = diff --git a/src/Handler/Utils/Submission.hs b/src/Handler/Utils/Submission.hs index bcda2d83c..b048177d3 100644 --- a/src/Handler/Utils/Submission.hs +++ b/src/Handler/Utils/Submission.hs @@ -15,8 +15,6 @@ import Import hiding (joinPath) import Jobs.Queue import Yesod.Core.Types (HandlerContents(..), ErrorResponse(..)) -import Utils.Lens - import Control.Monad.State as State (StateT) import Control.Monad.State.Class as State import Control.Monad.Writer (MonadWriter(..), execWriterT, execWriter) diff --git a/src/Handler/Utils/Table.hs b/src/Handler/Utils/Table.hs index 2acaf2a6a..626fa7e11 100644 --- a/src/Handler/Utils/Table.hs +++ b/src/Handler/Utils/Table.hs @@ -2,7 +2,6 @@ module Handler.Utils.Table where -- General Utilities for Tables import Import -import Data.Profunctor import Control.Monad.Except @@ -51,10 +50,12 @@ headedRowSelector toExternal fromExternal attrs colonnade tdata = do externalIds <- mapM (lift . toExternal) tdata let - checkbox extId = Field parse view UrlEncoded + checkbox extId = Field{..} where - parse [] _ = return $ Right Nothing - parse optlist _ = runExceptT $ do + fieldEnctype = UrlEncoded + + fieldParse [] _ = return $ Right Nothing + fieldParse optlist _ = runExceptT $ do extIds <- maybe (throwError "Error parsing values") return $ mapM fromPathPiece optlist case () of _ | extId `elem` extIds @@ -62,11 +63,11 @@ headedRowSelector toExternal fromExternal attrs colonnade tdata = do | otherwise -> return Nothing - view _ name attributes val _ = + fieldView theId name attributes val _ = -- TODO: move this to a *.hamlet file [whamlet|