From 01378476ec36caba98cebd874b340885609053ab Mon Sep 17 00:00:00 2001 From: Sergej Hoffmann <97111299+SevenWaysDP@users.noreply.github.com> Date: Fri, 12 Jan 2024 08:49:58 +0100 Subject: [PATCH 01/17] BC-5733 - Provide broken image if preview not possible (#2995) --- src/assets/img/image-not-available.svg | 6 ++ src/assets/img/index.d.ts | 4 + .../image-display/ImageDisplay.unit.ts | 11 +++ .../display/image-display/ImageDisplay.vue | 18 ++++- .../ui-preview-image/PreviewImage.unit.ts | 79 ++++++++++++++++--- .../ui-preview-image/PreviewImage.vue | 70 +++++++++++----- src/locales/de.json | 1 + src/locales/en.json | 1 + src/locales/es.json | 1 + src/locales/uk.json | 1 + 10 files changed, 160 insertions(+), 32 deletions(-) create mode 100644 src/assets/img/image-not-available.svg diff --git a/src/assets/img/image-not-available.svg b/src/assets/img/image-not-available.svg new file mode 100644 index 0000000000..1bd0fd0157 --- /dev/null +++ b/src/assets/img/image-not-available.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/img/index.d.ts b/src/assets/img/index.d.ts index db84834359..1294355bfd 100644 --- a/src/assets/img/index.d.ts +++ b/src/assets/img/index.d.ts @@ -2,3 +2,7 @@ declare module "@/assets/img/tldraw.png" { const value: string; export default value; } +declare module "@/assets/img/image-not-available.svg" { + const value: string; + export default value; +} diff --git a/src/components/feature-board-file-element/content/display/image-display/ImageDisplay.unit.ts b/src/components/feature-board-file-element/content/display/image-display/ImageDisplay.unit.ts index 1c64afce5e..a7419c89bb 100644 --- a/src/components/feature-board-file-element/content/display/image-display/ImageDisplay.unit.ts +++ b/src/components/feature-board-file-element/content/display/image-display/ImageDisplay.unit.ts @@ -92,6 +92,17 @@ describe("ImageDisplay", () => { ); }); + describe("when preview image emits error", () => { + it("should disable color overlay", async () => { + const { wrapper } = setup({ isEditMode: false }); + const previewImage = wrapper.find("previewimage-stub"); + await previewImage.vm.$emit("error"); + + const colorOverlay = wrapper.findComponent(ColorOverlay); + expect(colorOverlay.props("isOverlayDisabled")).toBe(true); + }); + }); + describe("when alternative text is defined", () => { it("should have set alt correctly", () => { const alternativeText = "alternative text"; diff --git a/src/components/feature-board-file-element/content/display/image-display/ImageDisplay.vue b/src/components/feature-board-file-element/content/display/image-display/ImageDisplay.vue index cfc15a52a4..c3df0ff39c 100644 --- a/src/components/feature-board-file-element/content/display/image-display/ImageDisplay.vue +++ b/src/components/feature-board-file-element/content/display/image-display/ImageDisplay.vue @@ -1,6 +1,6 @@ @@ -215,7 +216,7 @@ {{ $t("components.organisms.importUsers.legendUnMatched", { instance: $theme.name, - source: ldapSourceTranslation, + source: sourceSystemName, }) }} @@ -224,7 +225,7 @@ {{ $t("components.organisms.importUsers.legendAdminMatched", { instance: $theme.name, - source: ldapSourceTranslation, + source: sourceSystemName, }) }}
@@ -232,7 +233,7 @@ {{ $t("components.organisms.importUsers.legendAutoMatched", { instance: $theme.name, - source: ldapSourceTranslation, + source: sourceSystemName, }) }}
@@ -240,7 +241,7 @@ {{ $t("components.organisms.importUsers.legendFlag", { instance: $theme.name, - source: ldapSourceTranslation, + source: sourceSystemName, }) }}

@@ -252,7 +253,7 @@ diff --git a/src/components/error-handling/ErrorContent.vue b/src/components/error-handling/ErrorContent.vue index 289e8c0da4..a45764c5d3 100644 --- a/src/components/error-handling/ErrorContent.vue +++ b/src/components/error-handling/ErrorContent.vue @@ -6,7 +6,12 @@ fill="var(--v-primary-base)" data-testid="img-permission" /> - + import { computed, defineComponent } from "vue"; import PermissionErrorSvg from "@/assets/img/PermissionErrorSvg.vue"; +import NotFoundSvg from "@/assets/img/NotFoundSvg.vue"; import { HttpStatusCode } from "@/store/types/http-status-code.enum"; import { useTitle } from "@vueuse/core"; import { useI18n } from "@/composables/i18n.composable"; @@ -31,7 +37,7 @@ import { buildPageTitle } from "@/utils/pageTitle"; export default defineComponent({ name: "ErrorContent", - components: { PermissionErrorSvg }, + components: { PermissionErrorSvg, NotFoundSvg }, props: { errorText: String, statusCode: { @@ -54,8 +60,13 @@ export default defineComponent({ permissionErrorStatusCodes.includes(props.statusCode) ); + const isNotFoundError = computed( + () => props.statusCode === HttpStatusCode.NotFound + ); + return { isPermissionError, + isNotFoundError, }; }, }); diff --git a/src/locales/de.json b/src/locales/de.json index f6753f400e..b830f25a53 100644 --- a/src/locales/de.json +++ b/src/locales/de.json @@ -1098,5 +1098,11 @@ "page-class-members.title.info": "importiert aus einem externen System", "page-class-members.systemInfoText": "Daten der Klasse werden mit {systemName} synchronisiert. Die Klassenliste kann vorübergehend veraltet sein, bis sie mit dem neusten Stand in {systemName} abgeglichen wird. Die Daten werden nach jeder Anmeldung eines Klassenmitglieds in der Niedersächsischen Bildungscloud aktualisiert.", "page-class-members.classMembersInfoBox.title": "Schüler:innen sind noch nicht in der Niedersächsischen Bildungscloud?", - "page-class-members.classMembersInfoBox.text": "

Eine Einverständniserklärung bei der Registrierung von Schüler:innen muss nicht eingeholt werden. Die Nutzung der Niedersächsischen Bildungscloud ist im niedersächsischen Schulgesetz (§ 31 Abs. 5 NSchG) geregelt.

Falls die Schule die Daten der Nutzenden über ein externes System bezieht bzw. übermittelt bekommt, sind keine weiteren Schritte in der Cloud notwendig. Die Registrierung erfolgt über das externe System.

Anderenfalls können über den Verwaltungsbereich der Cloud Einladungen zur Registrierung per Link versendet werden:

" + "page-class-members.classMembersInfoBox.text": "

Eine Einverständniserklärung bei der Registrierung von Schüler:innen muss nicht eingeholt werden. Die Nutzung der Niedersächsischen Bildungscloud ist im niedersächsischen Schulgesetz (§ 31 Abs. 5 NSchG) geregelt.

Falls die Schule die Daten der Nutzenden über ein externes System bezieht bzw. übermittelt bekommt, sind keine weiteren Schritte in der Cloud notwendig. Die Registrierung erfolgt über das externe System.

Anderenfalls können über den Verwaltungsbereich der Cloud Einladungen zur Registrierung per Link versendet werden:

", + "tldraw.error.403": "Tldraw ist deaktiviert", + "tldraw.error.500": "Beim Einrichten von tldraw ist ein Fehler aufgetreten", + "tldraw.error.ws.4400": "Der Raumname fehlt in den URL-Parametern", + "tldraw.error.ws.4401": "Du hast keine Berechtigung für dieses Tldraw-Board", + "tldraw.error.ws.4404": "Tldraw-Board mit diesem Namen wurde nicht gefunden", + "tldraw.error.ws.4500": "Bei der Tldraw-Websocket-Verbindung ist ein Fehler aufgetreten" } diff --git a/src/locales/en.json b/src/locales/en.json index 1089717b42..4b1485c12b 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1097,5 +1097,11 @@ "page-class-members.title.info": "imported from an external system", "page-class-members.systemInfoText": "Class data is synchronized with {systemName}. The class list may be temporarily out of date until it is updated with the latest version in {systemName}. The data is updated every time a class member registers in the Niedersächsischen Bildungscloud.", "page-class-members.classMembersInfoBox.title": "Students are not yet in the Niedersächsischen Bildungscloud?", - "page-class-members.classMembersInfoBox.text": "

A declaration of consent does not need to be obtained when registering students. The use of the Niedersächsischen Bildungscloud is regulated in the Lower Saxony Schools Act (Section 31 Para. 5 NSchG).

If the school obtains or receives the user's data via an external system, no further steps are necessary in the cloud. Registration takes place via the external system.

Otherwise, invitations to register can be sent via link via the administration area of the cloud:

" + "page-class-members.classMembersInfoBox.text": "

A declaration of consent does not need to be obtained when registering students. The use of the Niedersächsischen Bildungscloud is regulated in the Lower Saxony Schools Act (Section 31 Para. 5 NSchG).

If the school obtains or receives the user's data via an external system, no further steps are necessary in the cloud. Registration takes place via the external system.

Otherwise, invitations to register can be sent via link via the administration area of the cloud:

", + "tldraw.error.403": "Tldraw is disabled", + "tldraw.error.500": "An error occured while setting up tldraw", + "tldraw.error.ws.4400": "Room name is missing in URL params", + "tldraw.error.ws.4401": "You do not have permission to this tldraw board", + "tldraw.error.ws.4404": "Tldraw board with this name was not found", + "tldraw.error.ws.4500": "An error occured within tldraw websocket connection" } diff --git a/src/locales/es.json b/src/locales/es.json index 7e78650fa4..93b65e7c89 100644 --- a/src/locales/es.json +++ b/src/locales/es.json @@ -1093,5 +1093,11 @@ "pages.h5p.api.success.save": "El contenido se ha guardado correctamente.", "page-class-members.systemInfoText": "Los datos de la clase se sincronizan con {systemName}. La lista de clases puede estar temporalmente desactualizada hasta que se actualice con la última versión en {systemName}. Los datos se actualizan cada vez que un miembro del grupo se registra en Niedersächsischen Bildungscloud.", "page-class-members.classMembersInfoBox.title": "¿Los estudiantes aún no están en la Niedersächsischen Bildungscloud?", - "page-class-members.classMembersInfoBox.text": "

No es necesario obtener una declaración de consentimiento al registrar estudiantes. El uso de Niedersächsischen Bildungscloud está regulado por la Ley de escuelas de Baja Sajonia (artículo 31, párrafo 5 de la NSchG).

Si la escuela obtiene o recibe los datos del usuario a través de un sistema externo, no es necesario realizar ningún otro paso en el proceso nube. El registro se realiza a través del sistema externo.

De lo contrario, las invitaciones para registrarse se pueden enviar mediante un enlace a través del área de administración de la nube:

" + "page-class-members.classMembersInfoBox.text": "

No es necesario obtener una declaración de consentimiento al registrar estudiantes. El uso de Niedersächsischen Bildungscloud está regulado por la Ley de escuelas de Baja Sajonia (artículo 31, párrafo 5 de la NSchG).

Si la escuela obtiene o recibe los datos del usuario a través de un sistema externo, no es necesario realizar ningún otro paso en el proceso nube. El registro se realiza a través del sistema externo.

De lo contrario, las invitaciones para registrarse se pueden enviar mediante un enlace a través del área de administración de la nube:

", + "tldraw.error.403": "Tldraw está desactivado", + "tldraw.error.500": "Se ha producido un error al configurar tldraw", + "tldraw.error.ws.4400": "Falta el nombre de la sala en los parámetros de la URL", + "tldraw.error.ws.4401": "Usted no tiene permiso para este tablero tldraw", + "tldraw.error.ws.4404": "No se ha encontrado el tablero Tldraw con este nombre", + "tldraw.error.ws.4500": "Se ha producido un error en la conexión tldraw websocket" } diff --git a/src/locales/uk.json b/src/locales/uk.json index 548b560f9d..47ec530c02 100644 --- a/src/locales/uk.json +++ b/src/locales/uk.json @@ -1123,5 +1123,11 @@ "pages.h5p.api.success.save": "Вміст успішно збережено.", "page-class-members.systemInfoText": "Дані класу синхронізуються з {systemName}. Список класів може бути тимчасово застарілим, поки його не буде оновлено останньою версією в {systemName}. Дані оновлюються кожного разу, коли учасник класу реєструється в Niedersächsischen Bildungscloud.", "page-class-members.classMembersInfoBox.title": "Студенти ще не в Niedersächsischen Bildungscloud?", - "page-class-members.classMembersInfoBox.text": "

Заява про згоду не потрібна під час реєстрації студентів. Використання Niedersächsischen Bildungscloud регулюється Законом про школи Нижньої Саксонії (розділ 31, параграф 5 NSchG).

Якщо школа отримує або отримує дані користувача через зовнішню систему, жодних подальших дій у хмара. Реєстрація відбувається через зовнішню систему.

Інакше запрошення до реєстрації можна надіслати за посиланням через область адміністрування хмари:

" + "page-class-members.classMembersInfoBox.text": "

Заява про згоду не потрібна під час реєстрації студентів. Використання Niedersächsischen Bildungscloud регулюється Законом про школи Нижньої Саксонії (розділ 31, параграф 5 NSchG).

Якщо школа отримує або отримує дані користувача через зовнішню систему, жодних подальших дій у хмара. Реєстрація відбувається через зовнішню систему.

Інакше запрошення до реєстрації можна надіслати за посиланням через область адміністрування хмари:

", + "tldraw.error.403": "Tldraw вимкнено", + "tldraw.error.500": "Виникла помилка під час налаштування tldraw", + "tldraw.error.ws.4400": "Назва кімнати відсутня в параметрах URL-адреси", + "tldraw.error.ws.4401": "Ви не маєте дозволу на цю дошку tldraw", + "tldraw.error.ws.4404": "Дошки Tldraw з такою назвою не знайдено", + "tldraw.error.ws.4500": "Виникла помилка під час з'єднання з веб-сокетом tldraw" } diff --git a/src/pages/Error.page.vue b/src/pages/Error.page.vue index 4ed37dbfb9..1eb1e82ff9 100644 --- a/src/pages/Error.page.vue +++ b/src/pages/Error.page.vue @@ -47,11 +47,16 @@ export default defineComponent({ )[0] as PerformanceNavigationTiming; const getError = () => { - if (performanceNavigation.type === "reload") { - const [statusCode, translationKey] = storage.getMultiple([ - "applicationErrorStatusCode", - "applicationErrorTranslationKey", - ]); + const [statusCode, translationKey, isTldrawError] = storage.getMultiple([ + "applicationErrorStatusCode", + "applicationErrorTranslationKey", + "applicationErrorTldraw", + ]); + + if ( + performanceNavigation.type === "reload" || + (performanceNavigation.type === "navigate" && isTldrawError) + ) { return { statusCode: Number(statusCode), translationKey, @@ -60,6 +65,8 @@ export default defineComponent({ storage.remove("applicationErrorStatusCode"); storage.remove("applicationErrorTranslationKey"); + storage.remove("applicationErrorTldraw"); + return { statusCode: Number(applicationErrorModule.getStatusCode), translationKey: applicationErrorModule.getTranslationKey, @@ -67,6 +74,7 @@ export default defineComponent({ }; addEventListener("pagehide", (event) => { + storage.remove("applicationErrorTldraw"); if (event.persisted) return; if (applicationErrorModule.getStatusCode) { From 77b4d4c4c7fad984b5c7ae2e682bd6a52e077887 Mon Sep 17 00:00:00 2001 From: agnisa-cap Date: Thu, 1 Feb 2024 08:04:18 +0100 Subject: [PATCH 16/17] N21-1712 changes migration wizard texts (#3062) --- .../molecules/vImportUsersMatchSearch.vue | 21 ++++++++++++++++--- src/locales/de.json | 12 ++++++----- src/locales/en.json | 9 +++++--- src/locales/es.json | 18 ++++++++++++++-- src/locales/uk.json | 15 +++++++------ 5 files changed, 56 insertions(+), 19 deletions(-) diff --git a/src/components/molecules/vImportUsersMatchSearch.vue b/src/components/molecules/vImportUsersMatchSearch.vue index 0644e11b25..78d1e74798 100644 --- a/src/components/molecules/vImportUsersMatchSearch.vue +++ b/src/components/molecules/vImportUsersMatchSearch.vue @@ -18,7 +18,7 @@ - + {{ $t("components.molecules.importUsersMatch.subtitle", { instance: $theme.name, @@ -26,6 +26,14 @@ }) }} + + {{ + $t("components.molecules.importUsersMatch.subtitle.nbc", { + instance: $theme.name, + source: ldapSource, + }) + }} + @@ -94,8 +102,15 @@ }} - {{ $t("components.molecules.importUsersMatch.unMatched") }} + + +
\nWeitere Informationen sind in unserem Hilfebereich zu externen Tools zu finden.", @@ -472,7 +472,7 @@ "pages.administration.migration.step5.afterSync.bullet2": "Neue Benutzerkonten sind angelegt worden.", "pages.administration.migration.step5.nbc.linkingFinished": "Die ausgewählten {instance}-Benutzerkonten wurden mit den entsprechenden {source}-Benutzerkonten verknüpft.", "pages.administration.migration.step5.nbc.bullet1": "Verknüpfte Benutzerkonten: Nutzer:innen können sich mit ihren {source}-Zugangsdaten einloggen. Die persönlichen Daten dieser Nutzer:innen werden aus {source} übernommen und ggf. bei jedem Login-Vorgang aktualisiert (Name, Geburtsdatum, Rolle, Klassen usw.).", - "pages.administration.migration.summary": "

Folgende Zuordnungen wurden vorgenommen:


{importUsersCount} {source}-Benutzerkonten sind einem {instance}-Benutzerkonto zugeordnet. Die Migration aus dem {source} wird durchgeführt.

{importUsersUnmatchedCount} {source}-Benutzerkonten sind keinem {instance}-Benutzerkonto zugeordnet. Die Konten aus dem {source} werden in der {instance} neu erstellt.

{usersUnmatchedCount} bereits vorhandene {instance}-Benutzerkonten sind keinem {source}-Benutzerkonto zugeordnet. Die {instance}-Benutzerkonten bleiben erhalten und können in der Verwaltung nachträglich gelöscht werden.

", + "pages.administration.migration.summary": "

Folgende Zuordnungen wurden vorgenommen:


{importUsersCount} {source}-Benutzerkonten sind einem {instance}-Benutzerkonto zugeordnet. Diese Benutzerkonten werden zu {source} migriert.

{importUsersUnmatchedCount} {source}-Benutzerkonten sind keinem {instance}-Benutzerkonto zugeordnet.

{usersUnmatchedCount} bereits vorhandene {instance}-Benutzerkonten sind keinem {source}-Benutzerkonto zugeordnet. Die {instance}-Benutzerkonten bleiben erhalten und können in der Verwaltung nachträglich gelöscht werden.

", "pages.administration.migration.title": "{source}-Konten mit bestehenden {instance}-Konten verknüpfen", "pages.administration.migration.tutorialWait": "Bitte beachten Sie, dass es nach Beginn der Schulmigration bis zu 1 Stunde dauern kann, bis die Daten abgerufen sind. Danach können Sie mit dem nächsten Schritt fortfahren.", "pages.administration.migration.waiting": "Warten auf die Datensynchronisation...", @@ -496,16 +496,18 @@ "components.organisms.importUsers.flagImportUser": "Nutzer markieren", "components.organisms.importUsers.createNew": "Neu erstellen", "components.organisms.importUsers.legend": "Erklärung der Filter-Symbole", - "components.organisms.importUsers.legendUnMatched": "Der {source}-Benutzer konnte in der {instance} nicht gefunden werden. In der {instance} wird dafür ein neues Benutzerkonto angelegt.", + "components.organisms.importUsers.legendUnMatched": "Der {source}-Benutzer konnte in der {instance} nicht gefunden werden. Nach Beendigung der Migration wird ein neues Benutzerkonto automatisch angelegt, sobald der moin.schule-Benutzer sich erstmalig bei der Niedersächsischen Bildungscloud anmeldet.", "components.organisms.importUsers.legendAdminMatched": "Das {source}-Benutzerkonto wurde manuell mit einem vorhandenen {instance}-Benutzerkonto verknüpft.", "components.organisms.importUsers.legendAutoMatched": "Das {source}-Benutzerkonto wurde automatisch mit einem vorhandenen {instance}-Benutzerkonto verknüpft.", - "components.organisms.importUsers.legendFlag": "Einträge, die markiert und ggf. später gesammelt überpüft werden sollen, können mit einer Flagge versehen werden.", + "components.organisms.importUsers.legendFlag": "Einträge, die markiert und ggf. später gesammelt überprüft werden sollen, können mit einer Flagge versehen werden.", "components.molecules.importUsersMatch.title": "Verknüpfe {source}-Konto mit {instance}-Konto", "components.molecules.importUsersMatch.subtitle": "Die {source}-Benutzerkonten werden später in die {instance} importiert. Wenn ein {source}-Benutzerkonto mit einem bestehenden {instance}-Benutzerkonto verknüpft werden soll, sodass die Benutzerdaten erhalten bleiben, kann das jeweilige {instance}-Konto hier ausgewählt werden. Andernfalls wird dieses als neues Benutzerkonto angelegt.", + "components.molecules.importUsersMatch.subtitle.nbc": "Die {source}-Benutzerkonten werden später in die {instance} importiert. Wenn ein {source}-Benutzerkonto mit einem bestehenden {instance}-Benutzerkonto verknüpft werden soll, sodass die Benutzerdaten erhalten bleiben, kann das jeweilige {instance}-Konto hier ausgewählt werden.", "components.molecules.importUsersMatch.flag": "Konto markieren", "components.molecules.importUsersMatch.saveMatch": "Verknüpfung speichern", "components.molecules.importUsersMatch.deleteMatch": "Verknüpfung löschen", "components.molecules.importUsersMatch.unMatched": "Keiner. Benutzer wird neu erstellt.", + "components.molecules.importUsersMatch.unMatched.nbc": "Keiner.", "components.molecules.importUsersMatch.search": "Benutzerkonto suchen", "components.molecules.importUsersMatch.write": "Vornamen oder Nachnamen eingeben", "components.molecules.importUsersMatch.notFound": "keine Konten gefunden", diff --git a/src/locales/en.json b/src/locales/en.json index 4b1485c12b..72cfd0544f 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -471,7 +471,7 @@ "pages.administration.migration.step5.afterSync.bullet2": "New user accounts have been created.", "pages.administration.migration.step5.nbc.linkingFinished": "The selected {instance} user accounts have been linked to the corresponding {source} user accounts.", "pages.administration.migration.step5.nbc.bullet1": "Linked user accounts: Users can log in with their {source} credentials. The personal data of these users is taken from {source} and, if necessary, updated with every login process (name, date of birth, role, classes, etc.).", - "pages.administration.migration.summary": "

The following assignments were made:


{importUsersCount} {source}-user accounts have a {instance} user account assigned. The {instance} user accounts will be migrated from the {source}-accounts.

{importUsersUnmatchedCount} {source}-user accounts have no associated {instance} user account. The {source} accounts will be newly created in {instance}.

{usersUnmatchedCount} {instance} user accounts were not assigned any {source}-accounts. The {instance} accounts are retained and can be subsequently deleted via the administration page (or contact user support).

", + "pages.administration.migration.summary": "

The following assignments were made:


{importUsersCount} {source}-user accounts have a {instance} user account assigned. These user accounts will be migrated to {source}.

{importUsersUnmatchedCount} {source}-user accounts have no associated {instance} user account.

{usersUnmatchedCount} {instance} user accounts were not assigned any {source}-accounts. The {instance} accounts are retained and can be subsequently deleted via the administration page (or contact user support).

", "pages.administration.migration.title": "Migrate user accounts", "pages.administration.migration.tutorialWait": "Please note, once the school migration process starts, it can take up to 1 hour to fetch the data. After this, you will be able to continue to the next step.", "pages.administration.migration.waiting": "Waiting for data sync...", @@ -495,15 +495,18 @@ "components.organisms.importUsers.flagImportUser": "Flag user", "components.organisms.importUsers.createNew": "Create new", "components.organisms.importUsers.legend": "Legend", - "components.organisms.importUsers.legendUnMatched": "{instance} account was not found. The {source} account will be newly created in the {instance}.", + "components.organisms.importUsers.legendUnMatched": "{instance} account was not found. After the migration is completed, a new user account will be created automatically as soon as the {source} user logs in to {instance} for the first time.", "components.organisms.importUsers.legendAdminMatched": "{source} account was manually linked to the {instance} account by an administrator.", "components.organisms.importUsers.legendAutoMatched": "{source} was automatically linked to the {instance} account.", "components.molecules.importUsersMatch.title": "Link {source} account to {instance} account", + "components.organisms.importUsers.legendFlag": "Entries that should be marked and, if necessary, checked later can be marked with a flag.", "components.molecules.importUsersMatch.subtitle": "The {source} account will be imported into the {instance} later. If the {source} account should be linked to an existing {instance} account, you can select the {instance} account here. Otherwise a new account will be created.", + "components.molecules.importUsersMatch.subtitle.nbc": "The {source} account will be imported into the {instance} later. If the {source} account should be linked to an existing {instance} account, you can select the {instance} account here.", "components.molecules.importUsersMatch.flag": "Flag account", "components.molecules.importUsersMatch.saveMatch": "Save relation", "components.molecules.importUsersMatch.deleteMatch": "Delete relation", - "components.molecules.importUsersMatch.unMatched": "none. Account will be newly created.", + "components.molecules.importUsersMatch.unMatched": "None. Account will be newly created.", + "components.molecules.importUsersMatch.unMatched.nbc": "None.", "components.molecules.importUsersMatch.search": "Search user", "components.molecules.importUsersMatch.write": "Input first and last name", "components.molecules.importUsersMatch.notFound": "no accounts found", diff --git a/src/locales/es.json b/src/locales/es.json index 93b65e7c89..af9d2a75ef 100644 --- a/src/locales/es.json +++ b/src/locales/es.json @@ -471,7 +471,7 @@ "pages.administration.migration.step5.afterSync.bullet2": "Se han creado cuentas de usuario nuevas.", "pages.administration.migration.step5.nbc.linkingFinished": "Las cuentas de usuario de {instance} seleccionadas se han vinculado a las cuentas de usuario de {source} correspondientes.", "pages.administration.migration.step5.nbc.bullet1": "Cuentas de usuario vinculadas: los usuarios pueden iniciar sesión con sus credenciales de {source}. Los datos personales de estos usuarios se obtienen de {source} y, si es necesario, se actualizan con cada proceso de inicio de sesión (nombre, fecha de nacimiento, rol, clases, etc.).", - "pages.administration.migration.summary": "

Se realizaron las siguientes asignaciones:


{importUsersCount} {source}-Benutzerkonten haben ein {instance} Benutzerkonto zugeordnet. Die {instance} Benutzerkonten werden auf die LDAP-Konten migriert

{importUsersUnmatchedCount} {source}-Benutzerkonten haben kein {instance} Benutzerkonto zugeordnet. Die {source} Konten werden neu in der {instance} erstellt.

{usersUnmatchedCount} {instance} Benutzerkonten wurden keinem {source}-Konto zugeordnet. Die {instance} Benutzerkonten bleiben erhalten und können über die Verwaltungsseite nachträglich gelöscht werden.

", + "pages.administration.migration.summary": "

Se realizaron las siguientes asignaciones:


{importUsersCount} {source}-las cuentas de usuario tienen una cuenta de usuario {instance} asignada. Estas cuentas de usuario se migrarán a {source}.

{importUsersUnmatchedCount} {source}: las cuentas de usuario no tienen ninguna cuenta de usuario de {instance} asociada.

{usersUnmatchedCount} A las cuentas de usuario de {instance} no se les asignó ninguna cuenta de {source}. Las cuentas de {instancia} se conservan y se pueden eliminar posteriormente a través de la página de administración (o comuníquese con el servicio de atención al usuario).

", "pages.administration.migration.title": "Migración de cuentas de usuario", "pages.administration.migration.tutorialWait": "Tenga en cuenta que, una vez que se inicie el proceso de migración de la escuela, puede tardar hasta 1 hora en obtener los datos. Después de esto, podrá continuar con el siguiente paso.", "pages.administration.migration.waiting": "Esperando la sincronización de datos...", @@ -495,7 +495,21 @@ "components.organisms.importUsers.flagImportUser": "Marcar usario", "components.organisms.importUsers.createNew": "Crear nuevo", "components.organisms.importUsers.legend": "Leyenda", - "components.organisms.importUsers.legendUnMatched": "{instance} usuario no encontrado. La cuenta de {source} está recién creada en {instance}.", + "components.organisms.importUsers.legendUnMatched": "{instance} usuario no encontrado. Una vez completada la migración, se creará automáticamente una nueva cuenta de usuario tan pronto como el usuario {source} inicie sesión en {instance} por primera vez.", + "components.organisms.importUsers.legendAdminMatched": "Un administrador vinculó manualmente la cuenta {source} a la cuenta {instance}.", + "components.organisms.importUsers.legendAutoMatched": "{source} se vinculó automáticamente a la cuenta de {instance}.", + "components.organisms.importUsers.legendFlag": "Las entradas que deben marcarse y, en caso necesario, comprobarse posteriormente se pueden marcar con una bandera.", + "components.molecules.importUsersMatch.title": "Vincular la cuenta {source} a la cuenta {instance}", + "components.molecules.importUsersMatch.subtitle": "Las cuentas de usuario {source} se importan posteriormente a la {instance}. Si desea vincular una cuenta de usuario de {source} a una cuenta de usuario de {instance} existente para conservar los datos del usuario, puede seleccionar aquí la cuenta de {instance} respectiva. De lo contrario, se creará como una nueva cuenta de usuario.", + "components.molecules.importUsersMatch.subtitle.nbc": "Las cuentas de usuario {source} se importan posteriormente a la {instance}. Si desea vincular una cuenta de usuario de {source} a una cuenta de usuario de {instance} existente para conservar los datos del usuario, puede seleccionar aquí la cuenta de {instance} respectiva.", + "components.molecules.importUsersMatch.flag": "Marcar cuenta", + "components.molecules.importUsersMatch.saveMatch": "Guardar enlace", + "components.molecules.importUsersMatch.deleteMatch": "Eliminar enlace", + "components.molecules.importUsersMatch.unMatched": "Ninguno. El usuario es recién creado.", + "components.molecules.importUsersMatch.unMatched.nbc": "Ninguno.", + "components.molecules.importUsersMatch.search": "Buscar cuenta de usuario", + "components.molecules.importUsersMatch.write": "Introduzca nombre o apellido", + "components.molecules.importUsersMatch.notFound": "No se encontraron cuentas", "components.molecules.copyResult.title.loading": "Copiando en proceso...", "components.molecules.copyResult.title.success": "Copia exitosa", "components.molecules.copyResult.title.partial": "Información importante sobre la copia", diff --git a/src/locales/uk.json b/src/locales/uk.json index 47ec530c02..c306c38f20 100644 --- a/src/locales/uk.json +++ b/src/locales/uk.json @@ -326,9 +326,11 @@ "components.molecules.import.tasks.options.selectCourse": "Оберіть курс", "components.molecules.importUsersMatch.saveMatch": "Зберегти зв'язок", "components.molecules.importUsersMatch.search": "Пошук користувача", - "components.molecules.importUsersMatch.subtitle": "Das webbschule-Konto wird später in die {instance} importiert. Wenn Sie das Konto mit einem bestehenden {instance} Konto verknüpfen möchten, so dass die Benutzerdaten erhalten bleiben, wählen Sie hier das {instance} Konto aus. Andernfalls wird dieses als neues Konto angeleg.", - "components.molecules.importUsersMatch.title": "Verknüpfe {source} Konto mit {instance} Konto", + "components.molecules.importUsersMatch.subtitle": "Пізніше обліковий запис webbschule буде імпортовано в {instance}. Якщо ви хочете зв’язати обліковий запис із наявним обліковим записом {instance}, щоб зберегти дані користувача, виберіть тут обліковий запис {instance}. В іншому випадку цей обліковий запис буде створено як новий.", + "components.molecules.importUsersMatch.subtitle.nbc": "Пізніше обліковий запис webbschule буде імпортовано в {instance}. Якщо ви хочете зв’язати обліковий запис із наявним обліковим записом {instance}, щоб зберегти дані користувача, виберіть тут обліковий запис {instance}.", + "components.molecules.importUsersMatch.title": "Пов’яжіть обліковий запис {source} з обліковим записом {instance}", "components.molecules.importUsersMatch.unMatched": "немає. Обліковий запис буде створено знову.", + "components.molecules.importUsersMatch.unMatched.nbc": "немає.", "components.molecules.importUsersMatch.write": "Введіть ім'я та прізвище", "components.molecules.MintEcFooter.chapters": "Огляд розділу", "components.molecules.TaskItemMenu.confirmDelete.text": "Ви впевнені, що хочете видалити завдання \" {taskTitle} \"?", @@ -453,9 +455,10 @@ "components.organisms.importUsers.editImportUser": "Редагувати користувача", "components.organisms.importUsers.flagImportUser": "позначати користувачів", "components.organisms.importUsers.legend": "Легенда", - "components.organisms.importUsers.legendAdminMatched": "{source} Konto von Administrator mit {instance} Benutzer verknüpft.", - "components.organisms.importUsers.legendAutoMatched": "{source} Konto automatisch mit {instance} Benutzer verknüpft.", - "components.organisms.importUsers.legendUnMatched": "{instance} Benutzer nicht gefunden. Das {source} Konto wird neu in {instance} erstellt.", + "components.organisms.importUsers.legendAdminMatched": "{source} Обліковий запис адміністратора, пов’язаний із користувачем {instance}.", + "components.organisms.importUsers.legendAutoMatched": "Обліковий запис {source} автоматично пов’язано з користувачем {instance}.", + "components.organisms.importUsers.legendUnMatched": "{instance} Користувача не знайдено. Після завершення міграції новий обліковий запис користувача буде створено автоматично, щойно користувач {source} уперше ввійде до {instance}.", + "components.organisms.importUsers.legendFlag": "Записи, які слід позначити та, за потреби, перевірити пізніше, можна позначити прапорцем.", "components.organisms.importUsers.roleAdministrator": "Адміністратор", "components.organisms.importUsers.searchAutoMatched": "Фільтрувати за автоматично зіставленими", "components.organisms.importUsers.searchClass": "Пошук класу", @@ -674,7 +677,7 @@ "pages.administration.migration.step5.afterSync.bullet2": "Створено нові облікові записи користувачів.", "pages.administration.migration.step5.nbc.linkingFinished": "Вибрані облікові записи користувачів {instance} було пов’язано з відповідними обліковими записами користувачів {source}.", "pages.administration.migration.step5.nbc.bullet1": "Пов’язані облікові записи користувачів: користувачі можуть входити за допомогою своїх облікових даних {source}. Особисті дані цих користувачів беруться з {source} і, якщо необхідно, оновлюються під час кожного процесу входу (ім’я, дата народження, роль, класи тощо).", - "pages.administration.migration.summary": "

Було зроблено такі зіставлення:


{importUsersCount} облікові записи користувачів {source} зіставили обліковий запис користувача {instance}. Облікові записи користувачів {instance} буде переміщено до облікових записів LDAP

{importUsersUnmatchedCount} Облікові записи користувачів {source} не мають пов’язаного облікового запису користувача {instance}. Облікові записи {source} буде відтворено в {instance}.

{usersUnmatchedCount} {instance} облікових записів користувачів не було зіставлено з обліковим записом {source}. Облікові записи користувачів {instance} зберігаються та можуть бути згодом видалені на сторінці адміністрування.

", + "pages.administration.migration.summary": "

Було зроблено такі призначення:


{importUsersCount} {source}-облікові записи користувачів мають призначений обліковий запис користувача {instance}. Ці облікові записи користувачів буде переміщено до {source}.

{importUsersUnmatchedCount} {source}-облікові записи користувачів не мають пов’язаного облікового запису користувача {instance}.

{usersUnmatchedCount} {instance} обліковим записам користувачів не було призначено облікових записів {source}. Облікові записи {instance} зберігаються та можуть бути згодом видалені на сторінці адміністрування (або зв’язатися зі службою підтримки користувачів).

", "pages.administration.migration.title": "Перенести облікові записи користувачів із", "pages.administration.migration.tutorialWait": "Зауважте, що після початку переміщення школи для отримання даних може знадобитися до 1 години. Після цього можна переходити до наступного кроку.", "pages.administration.migration.waiting": "Очікування синхронізації даних...", From 84f27bfdb98b40cca18ae68cdf3e9f53f1f77aff Mon Sep 17 00:00:00 2001 From: Igor Richter <93926487+IgorCapCoder@users.noreply.github.com> Date: Fri, 2 Feb 2024 15:17:26 +0100 Subject: [PATCH 17/17] N21-1626 ctl data sheet (#3063) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * datasheet button, server call and generate api --------- Co-authored-by: Arne Gnisa Co-authored-by: Marvin Öhlerking --- .../ExternalToolSection.unit.ts | 82 +++++++++++++------ .../administration/ExternalToolSection.vue | 19 ++++- .../ExternalToolToolbar.unit.ts | 12 ++- .../administration/ExternalToolToolbar.vue | 30 ++++++- .../external-tool-section-utils.composable.ts | 5 +- ...rnal-tool-section-utils.composable.unit.ts | 43 ++++++---- .../school-external-tool-item.ts | 3 + src/locales/de.json | 1 + src/locales/en.json | 1 + src/locales/es.json | 1 + src/locales/uk.json | 1 + src/serverApi/v3/api.ts | 81 ++++++++++++++++++ src/store/school-external-tools.ts | 4 +- src/store/school-external-tools.unit.ts | 18 ++-- 14 files changed, 238 insertions(+), 63 deletions(-) diff --git a/src/components/administration/ExternalToolSection.unit.ts b/src/components/administration/ExternalToolSection.unit.ts index 5761e0ac91..a11ba9f12a 100644 --- a/src/components/administration/ExternalToolSection.unit.ts +++ b/src/components/administration/ExternalToolSection.unit.ts @@ -10,18 +10,18 @@ import { } from "@/utils/inject"; import { createModuleMocks } from "@/utils/mock-store-module"; import createComponentMocks from "@@/tests/test-utils/componentMocks"; -import { i18nMock } from "@@/tests/test-utils/i18nMock"; -import { mdiAlert, mdiCheckCircle } from "@mdi/js"; -import { mount, Wrapper } from "@vue/test-utils"; -import Vue, { ref } from "vue"; -import ExternalToolSection from "./ExternalToolSection.vue"; -import { createMock, DeepMocked } from "@golevelup/ts-jest"; -import { useSchoolExternalToolUsage } from "@data-external-tool"; import { schoolExternalToolFactory, schoolExternalToolMetadataFactory, schoolToolConfigurationStatusFactory, } from "@@/tests/test-utils/factory"; +import { i18nMock } from "@@/tests/test-utils/i18nMock"; +import { useSchoolExternalToolUsage } from "@data-external-tool"; +import { createMock, DeepMocked } from "@golevelup/ts-jest"; +import { mdiAlert, mdiCheckCircle } from "@mdi/js"; +import { mount, Wrapper } from "@vue/test-utils"; +import Vue, { ref } from "vue"; +import ExternalToolSection from "./ExternalToolSection.vue"; jest.mock("@data-external-tool"); @@ -32,6 +32,9 @@ describe("ExternalToolSection", () => { ReturnType >; + const createDatasheetButtonIndex = 1; + const deleteButtonIndex = 2; + const getWrapper = (getters: Partial = {}) => { el = document.createElement("div"); el.setAttribute("data-app", "true"); @@ -111,18 +114,20 @@ describe("ExternalToolSection", () => { const setupItems = () => { const firstToolName = "Test"; const secondToolName = "Test2"; + const schoolExternalTool = schoolExternalToolFactory.build({ + id: "testId", + toolId: "toolId", + schoolId: "schoolId", + parameters: [], + name: firstToolName, + status: schoolToolConfigurationStatusFactory.build(), + version: 1, + isDeactivated: false, + }); + const { wrapper, schoolExternalToolsModule } = getWrapper({ getSchoolExternalTools: [ - { - id: "testId", - toolId: "toolId", - schoolId: "schoolId", - parameters: [], - name: firstToolName, - status: schoolToolConfigurationStatusFactory.build(), - version: 1, - isDeactivated: false, - }, + schoolExternalTool, { id: "testId2", toolId: "toolId", @@ -149,11 +154,15 @@ describe("ExternalToolSection", () => { }, ], }); + + jest.spyOn(window, "open"); + return { wrapper, schoolExternalToolsModule, firstToolName, secondToolName, + schoolExternalTool, }; }; @@ -231,6 +240,27 @@ describe("ExternalToolSection", () => { expect( firstRowButtons.at(1).classes().includes("v-btn--icon") ).toBeTruthy(); + expect( + firstRowButtons.at(2).classes().includes("v-btn--icon") + ).toBeTruthy(); + }); + + it("should open a new tab with click on create datasheet", async () => { + const { wrapper, schoolExternalTool } = setupItems(); + const toolId = schoolExternalTool.toolId; + + const tableRows = wrapper.find("tbody").findAll("tr"); + const firstRowButtons = tableRows.at(0).findAll("button"); + const datasheetButton = firstRowButtons.at( + createDatasheetButtonIndex + ); + + await datasheetButton.trigger("click"); + await Vue.nextTick(); + + expect(window.open).toHaveBeenCalledWith( + `/api/v3/tools/external-tools/${toolId}/datasheet` + ); }); it("a dialog should be displayed with click on delete", async () => { @@ -238,7 +268,7 @@ describe("ExternalToolSection", () => { const tableRows = wrapper.find("tbody").findAll("tr"); const firstRowButtons = tableRows.at(0).findAll("button"); - const deleteButton = firstRowButtons.at(1); + const deleteButton = firstRowButtons.at(deleteButtonIndex); await deleteButton.trigger("click"); @@ -254,7 +284,7 @@ describe("ExternalToolSection", () => { const tableRows = wrapper.find("tbody").findAll("tr"); const firstRowButtons = tableRows.at(0).findAll("button"); - const deleteButton = firstRowButtons.at(1); + const deleteButton = firstRowButtons.at(deleteButtonIndex); await deleteButton.trigger("click"); @@ -283,7 +313,7 @@ describe("ExternalToolSection", () => { const firstRowButtons = tableRows.at(0).findAll("button"); - const deleteButton = firstRowButtons.at(1); + const deleteButton = firstRowButtons.at(deleteButtonIndex); await deleteButton.trigger("click"); const confirmButton = wrapper.find( @@ -316,7 +346,7 @@ describe("ExternalToolSection", () => { const firstRowButtons = tableRows.at(0).findAll("button"); - const deleteButton = firstRowButtons.at(1); + const deleteButton = firstRowButtons.at(deleteButtonIndex); await deleteButton.trigger("click"); const confirmButton = wrapper.find( @@ -401,7 +431,7 @@ describe("ExternalToolSection", () => { const firstRowButtons = tableRows.at(0).findAll("button"); - const deleteButton = firstRowButtons.at(1); + const deleteButton = firstRowButtons.at(deleteButtonIndex); await deleteButton.trigger("click"); const dialog = wrapper.find('[data-testid="delete-dialog"]'); @@ -416,7 +446,7 @@ describe("ExternalToolSection", () => { const firstRowButtons = tableRows.at(0).findAll("button"); - const deleteButton = firstRowButtons.at(1); + const deleteButton = firstRowButtons.at(deleteButtonIndex); await deleteButton.trigger("click"); const dialogContent = wrapper.find( @@ -435,7 +465,7 @@ describe("ExternalToolSection", () => { const firstRowButtons = tableRows.at(0).findAll("button"); - const deleteButton = firstRowButtons.at(1); + const deleteButton = firstRowButtons.at(deleteButtonIndex); await deleteButton.trigger("click"); expect(notifierModule.show).not.toHaveBeenCalled(); @@ -466,7 +496,7 @@ describe("ExternalToolSection", () => { const firstRowButtons = tableRows.at(0).findAll("button"); - const deleteButton = firstRowButtons.at(1); + const deleteButton = firstRowButtons.at(deleteButtonIndex); await deleteButton.trigger("click"); const dialog = wrapper.find('[data-testid="delete-dialog"]'); @@ -481,7 +511,7 @@ describe("ExternalToolSection", () => { const firstRowButtons = tableRows.at(0).findAll("button"); - const deleteButton = firstRowButtons.at(1); + const deleteButton = firstRowButtons.at(deleteButtonIndex); await deleteButton.trigger("click"); expect(notifierModule.show).toHaveBeenCalledWith({ diff --git a/src/components/administration/ExternalToolSection.vue b/src/components/administration/ExternalToolSection.vue index ec6ad50f13..644d729ca1 100644 --- a/src/components/administration/ExternalToolSection.vue +++ b/src/components/administration/ExternalToolSection.vue @@ -32,8 +32,9 @@ @@ -105,7 +106,8 @@