diff --git a/src/components/administration/AdminMigrationSection.unit.ts b/src/components/administration/AdminMigrationSection.unit.ts index 3c1f66322b..c8b3554cc5 100644 --- a/src/components/administration/AdminMigrationSection.unit.ts +++ b/src/components/administration/AdminMigrationSection.unit.ts @@ -60,7 +60,8 @@ describe("AdminMigrationSection", () => { }); envConfigModule = createModuleMocks(EnvConfigModule, { - getAccessibilityReportEmail: "nbc-support@netz-21.de", + getAccessibilityReportEmail: "ticketsystem@niedersachsen.support", + getSupportProblemEmail: "ticketsystem@niedersachsen.support", getEnv: {} as ConfigResponse, ...envConfigGetters, }); diff --git a/src/components/administration/AdminMigrationSection.vue b/src/components/administration/AdminMigrationSection.vue index dfa7b6945d..09172e9b82 100644 --- a/src/components/administration/AdminMigrationSection.vue +++ b/src/components/administration/AdminMigrationSection.vue @@ -66,7 +66,7 @@ tag="p" > {{ t("components.administrationSection.description.support.link") }} diff --git a/src/pages/user-login-migration/UserLoginMigrationError.page.unit.ts b/src/pages/user-login-migration/UserLoginMigrationError.page.unit.ts index cc16993ac8..93fcd87dba 100644 --- a/src/pages/user-login-migration/UserLoginMigrationError.page.unit.ts +++ b/src/pages/user-login-migration/UserLoginMigrationError.page.unit.ts @@ -46,7 +46,7 @@ describe("UserLoginMigrationError", () => { getSystems: systemsMock, }); envConfigModule = createModuleMocks(EnvConfigModule, { - getAccessibilityReportEmail: "nbc-support@netz-21.de", + getAccessibilityReportEmail: "ticketsystem@niedersachsen.support", }); userLoginMigrationModule = createModuleMocks(UserLoginMigrationModule, { getUserLoginMigration: userLoginMigrationFactory.build(), @@ -142,7 +142,7 @@ describe("UserLoginMigrationError", () => { "pages.userMigration.error.description.support.link" ); expect(supportLink.element.href).toEqual( - "mailto:nbc-support@netz-21.de?subject=Schulnummer%20nicht%20korrekt" + "mailto:ticketsystem@niedersachsen.support?subject=Schulnummer%20nicht%20korrekt" ); }); }); diff --git a/src/serverApi/v3/api.ts b/src/serverApi/v3/api.ts index 054e31a0de..f2368f57c0 100644 --- a/src/serverApi/v3/api.ts +++ b/src/serverApi/v3/api.ts @@ -1015,6 +1015,16 @@ export interface ClassInfoSearchListResponse { */ limit: number; } +/** + * + * @export + * @enum {string} + */ +export enum ClassRequestContext { + Course = 'course', + ClassOverview = 'class-overview' +} + /** * * @export @@ -1277,6 +1287,12 @@ export interface ConfigResponse { * @memberof ConfigResponse */ ACCESSIBILITY_REPORT_EMAIL: string; + /** + * + * @type {string} + * @memberof ConfigResponse + */ + SUPPORT_PROBLEM_EMAIL_ADDRESS: string; /** * * @type {boolean} @@ -16694,10 +16710,11 @@ export const GroupApiAxiosParamCreator = function (configuration?: Configuration * @param {'asc' | 'desc'} [sortOrder] * @param {ClassSortQueryType} [sortBy] * @param {SchoolYearQueryType} [type] + * @param {ClassRequestContext} [calledFrom] * @param {*} [options] Override http request option. * @throws {RequiredError} */ - groupControllerFindClasses: async (skip?: number, limit?: number, sortOrder?: 'asc' | 'desc', sortBy?: ClassSortQueryType, type?: SchoolYearQueryType, options: any = {}): Promise => { + groupControllerFindClasses: async (skip?: number, limit?: number, sortOrder?: 'asc' | 'desc', sortBy?: ClassSortQueryType, type?: SchoolYearQueryType, calledFrom?: ClassRequestContext, options: any = {}): Promise => { const localVarPath = `/groups/class`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); @@ -16734,6 +16751,10 @@ export const GroupApiAxiosParamCreator = function (configuration?: Configuration localVarQueryParameter['type'] = type; } + if (calledFrom !== undefined) { + localVarQueryParameter['calledFrom'] = calledFrom; + } + setSearchParams(localVarUrlObj, localVarQueryParameter, options.query); @@ -16855,11 +16876,12 @@ export const GroupApiFp = function(configuration?: Configuration) { * @param {'asc' | 'desc'} [sortOrder] * @param {ClassSortQueryType} [sortBy] * @param {SchoolYearQueryType} [type] + * @param {ClassRequestContext} [calledFrom] * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async groupControllerFindClasses(skip?: number, limit?: number, sortOrder?: 'asc' | 'desc', sortBy?: ClassSortQueryType, type?: SchoolYearQueryType, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.groupControllerFindClasses(skip, limit, sortOrder, sortBy, type, options); + async groupControllerFindClasses(skip?: number, limit?: number, sortOrder?: 'asc' | 'desc', sortBy?: ClassSortQueryType, type?: SchoolYearQueryType, calledFrom?: ClassRequestContext, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.groupControllerFindClasses(skip, limit, sortOrder, sortBy, type, calledFrom, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** @@ -16905,11 +16927,12 @@ export const GroupApiFactory = function (configuration?: Configuration, basePath * @param {'asc' | 'desc'} [sortOrder] * @param {ClassSortQueryType} [sortBy] * @param {SchoolYearQueryType} [type] + * @param {ClassRequestContext} [calledFrom] * @param {*} [options] Override http request option. * @throws {RequiredError} */ - groupControllerFindClasses(skip?: number, limit?: number, sortOrder?: 'asc' | 'desc', sortBy?: ClassSortQueryType, type?: SchoolYearQueryType, options?: any): AxiosPromise { - return localVarFp.groupControllerFindClasses(skip, limit, sortOrder, sortBy, type, options).then((request) => request(axios, basePath)); + groupControllerFindClasses(skip?: number, limit?: number, sortOrder?: 'asc' | 'desc', sortBy?: ClassSortQueryType, type?: SchoolYearQueryType, calledFrom?: ClassRequestContext, options?: any): AxiosPromise { + return localVarFp.groupControllerFindClasses(skip, limit, sortOrder, sortBy, type, calledFrom, options).then((request) => request(axios, basePath)); }, /** * @@ -16951,11 +16974,12 @@ export interface GroupApiInterface { * @param {'asc' | 'desc'} [sortOrder] * @param {ClassSortQueryType} [sortBy] * @param {SchoolYearQueryType} [type] + * @param {ClassRequestContext} [calledFrom] * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof GroupApiInterface */ - groupControllerFindClasses(skip?: number, limit?: number, sortOrder?: 'asc' | 'desc', sortBy?: ClassSortQueryType, type?: SchoolYearQueryType, options?: any): AxiosPromise; + groupControllerFindClasses(skip?: number, limit?: number, sortOrder?: 'asc' | 'desc', sortBy?: ClassSortQueryType, type?: SchoolYearQueryType, calledFrom?: ClassRequestContext, options?: any): AxiosPromise; /** * @@ -16997,12 +17021,13 @@ export class GroupApi extends BaseAPI implements GroupApiInterface { * @param {'asc' | 'desc'} [sortOrder] * @param {ClassSortQueryType} [sortBy] * @param {SchoolYearQueryType} [type] + * @param {ClassRequestContext} [calledFrom] * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof GroupApi */ - public groupControllerFindClasses(skip?: number, limit?: number, sortOrder?: 'asc' | 'desc', sortBy?: ClassSortQueryType, type?: SchoolYearQueryType, options?: any) { - return GroupApiFp(this.configuration).groupControllerFindClasses(skip, limit, sortOrder, sortBy, type, options).then((request) => request(this.axios, this.basePath)); + public groupControllerFindClasses(skip?: number, limit?: number, sortOrder?: 'asc' | 'desc', sortBy?: ClassSortQueryType, type?: SchoolYearQueryType, calledFrom?: ClassRequestContext, options?: any) { + return GroupApiFp(this.configuration).groupControllerFindClasses(skip, limit, sortOrder, sortBy, type, calledFrom, options).then((request) => request(this.axios, this.basePath)); } /** @@ -21964,10 +21989,12 @@ export const SchoolApiAxiosParamCreator = function (configuration?: Configuratio /** * * @param {string} schoolId + * @param {number} [skip] Number of elements (not pages) to be skipped + * @param {number} [limit] Page limit, defaults to 10. * @param {*} [options] Override http request option. * @throws {RequiredError} */ - schoolControllerGetTeachers: async (schoolId: string, options: any = {}): Promise => { + schoolControllerGetTeachers: async (schoolId: string, skip?: number, limit?: number, options: any = {}): Promise => { // verify required parameter 'schoolId' is not null or undefined assertParamExists('schoolControllerGetTeachers', 'schoolId', schoolId) const localVarPath = `/school/{schoolId}/teachers` @@ -21987,6 +22014,14 @@ export const SchoolApiAxiosParamCreator = function (configuration?: Configuratio // http bearer authentication required await setBearerAuthToObject(localVarHeaderParameter, configuration) + if (skip !== undefined) { + localVarQueryParameter['skip'] = skip; + } + + if (limit !== undefined) { + localVarQueryParameter['limit'] = limit; + } + setSearchParams(localVarUrlObj, localVarQueryParameter, options.query); @@ -22204,11 +22239,13 @@ export const SchoolApiFp = function(configuration?: Configuration) { /** * * @param {string} schoolId + * @param {number} [skip] Number of elements (not pages) to be skipped + * @param {number} [limit] Page limit, defaults to 10. * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async schoolControllerGetTeachers(schoolId: string, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.schoolControllerGetTeachers(schoolId, options); + async schoolControllerGetTeachers(schoolId: string, skip?: number, limit?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.schoolControllerGetTeachers(schoolId, skip, limit, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** @@ -22314,11 +22351,13 @@ export const SchoolApiFactory = function (configuration?: Configuration, basePat /** * * @param {string} schoolId + * @param {number} [skip] Number of elements (not pages) to be skipped + * @param {number} [limit] Page limit, defaults to 10. * @param {*} [options] Override http request option. * @throws {RequiredError} */ - schoolControllerGetTeachers(schoolId: string, options?: any): AxiosPromise { - return localVarFp.schoolControllerGetTeachers(schoolId, options).then((request) => request(axios, basePath)); + schoolControllerGetTeachers(schoolId: string, skip?: number, limit?: number, options?: any): AxiosPromise { + return localVarFp.schoolControllerGetTeachers(schoolId, skip, limit, options).then((request) => request(axios, basePath)); }, /** * @@ -22419,11 +22458,13 @@ export interface SchoolApiInterface { /** * * @param {string} schoolId + * @param {number} [skip] Number of elements (not pages) to be skipped + * @param {number} [limit] Page limit, defaults to 10. * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof SchoolApiInterface */ - schoolControllerGetTeachers(schoolId: string, options?: any): AxiosPromise; + schoolControllerGetTeachers(schoolId: string, skip?: number, limit?: number, options?: any): AxiosPromise; /** * @@ -22536,12 +22577,14 @@ export class SchoolApi extends BaseAPI implements SchoolApiInterface { /** * * @param {string} schoolId + * @param {number} [skip] Number of elements (not pages) to be skipped + * @param {number} [limit] Page limit, defaults to 10. * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof SchoolApi */ - public schoolControllerGetTeachers(schoolId: string, options?: any) { - return SchoolApiFp(this.configuration).schoolControllerGetTeachers(schoolId, options).then((request) => request(this.axios, this.basePath)); + public schoolControllerGetTeachers(schoolId: string, skip?: number, limit?: number, options?: any) { + return SchoolApiFp(this.configuration).schoolControllerGetTeachers(schoolId, skip, limit, options).then((request) => request(this.axios, this.basePath)); } /** @@ -27212,11 +27255,10 @@ export const UserImportApiAxiosParamCreator = function (configuration?: Configur /** * Populates import users from specific user migration populate endpoint. * @summary Populates import users - * @param {boolean} [matchByPreferredName] Should the users preferred name from the external system be used for auto-matching to existing users? * @param {*} [options] Override http request option. * @throws {RequiredError} */ - importUserControllerPopulateImportUsers: async (matchByPreferredName?: boolean, options: any = {}): Promise => { + importUserControllerPopulateImportUsers: async (options: any = {}): Promise => { const localVarPath = `/user/import/populate-import-users`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); @@ -27233,10 +27275,6 @@ export const UserImportApiAxiosParamCreator = function (configuration?: Configur // http bearer authentication required await setBearerAuthToObject(localVarHeaderParameter, configuration) - if (matchByPreferredName !== undefined) { - localVarQueryParameter['matchByPreferredName'] = matchByPreferredName; - } - setSearchParams(localVarUrlObj, localVarQueryParameter, options.query); @@ -27518,12 +27556,11 @@ export const UserImportApiFp = function(configuration?: Configuration) { /** * Populates import users from specific user migration populate endpoint. * @summary Populates import users - * @param {boolean} [matchByPreferredName] Should the users preferred name from the external system be used for auto-matching to existing users? * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async importUserControllerPopulateImportUsers(matchByPreferredName?: boolean, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.importUserControllerPopulateImportUsers(matchByPreferredName, options); + async importUserControllerPopulateImportUsers(options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.importUserControllerPopulateImportUsers(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** @@ -27646,12 +27683,11 @@ export const UserImportApiFactory = function (configuration?: Configuration, bas /** * Populates import users from specific user migration populate endpoint. * @summary Populates import users - * @param {boolean} [matchByPreferredName] Should the users preferred name from the external system be used for auto-matching to existing users? * @param {*} [options] Override http request option. * @throws {RequiredError} */ - importUserControllerPopulateImportUsers(matchByPreferredName?: boolean, options?: any): AxiosPromise { - return localVarFp.importUserControllerPopulateImportUsers(matchByPreferredName, options).then((request) => request(axios, basePath)); + importUserControllerPopulateImportUsers(options?: any): AxiosPromise { + return localVarFp.importUserControllerPopulateImportUsers(options).then((request) => request(axios, basePath)); }, /** * @@ -27767,12 +27803,11 @@ export interface UserImportApiInterface { /** * Populates import users from specific user migration populate endpoint. * @summary Populates import users - * @param {boolean} [matchByPreferredName] Should the users preferred name from the external system be used for auto-matching to existing users? * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof UserImportApiInterface */ - importUserControllerPopulateImportUsers(matchByPreferredName?: boolean, options?: any): AxiosPromise; + importUserControllerPopulateImportUsers(options?: any): AxiosPromise; /** * @@ -27898,13 +27933,12 @@ export class UserImportApi extends BaseAPI implements UserImportApiInterface { /** * Populates import users from specific user migration populate endpoint. * @summary Populates import users - * @param {boolean} [matchByPreferredName] Should the users preferred name from the external system be used for auto-matching to existing users? * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof UserImportApi */ - public importUserControllerPopulateImportUsers(matchByPreferredName?: boolean, options?: any) { - return UserImportApiFp(this.configuration).importUserControllerPopulateImportUsers(matchByPreferredName, options).then((request) => request(this.axios, this.basePath)); + public importUserControllerPopulateImportUsers(options?: any) { + return UserImportApiFp(this.configuration).importUserControllerPopulateImportUsers(options).then((request) => request(this.axios, this.basePath)); } /** diff --git a/src/store/env-config-defaults.ts b/src/store/env-config-defaults.ts index 81c74c84fb..aff6f21f92 100644 --- a/src/store/env-config-defaults.ts +++ b/src/store/env-config-defaults.ts @@ -30,6 +30,7 @@ export const defaultConfigEnvs: ConfigResponse = { FEATURE_CTL_TOOLS_TAB_ENABLED: false, FEATURE_CTL_TOOLS_COPY_ENABLED: false, ACCESSIBILITY_REPORT_EMAIL: "", + SUPPORT_PROBLEM_EMAIL_ADDRESS: "", FEATURE_NEW_SCHOOL_ADMINISTRATION_PAGE_AS_DEFAULT_ENABLED: false, FEATURE_LTI_TOOLS_TAB_ENABLED: true, FEATURE_SHOW_MIGRATION_WIZARD: false, diff --git a/src/store/env-config.ts b/src/store/env-config.ts index 9afb2b85f4..adeef7bab4 100644 --- a/src/store/env-config.ts +++ b/src/store/env-config.ts @@ -144,6 +144,10 @@ export default class EnvConfigModule extends VuexModule { return this.env.GHOST_BASE_URL; } + get getSupportProblemEmail(): string { + return this.env.SUPPORT_PROBLEM_EMAIL_ADDRESS; + } + get getAccessibilityReportEmail(): string { return this.env.ACCESSIBILITY_REPORT_EMAIL; } diff --git a/src/themes/n21/components/legacy/TheFooter.vue b/src/themes/n21/components/legacy/TheFooter.vue index 60216f4997..215061e31f 100644 --- a/src/themes/n21/components/legacy/TheFooter.vue +++ b/src/themes/n21/components/legacy/TheFooter.vue @@ -58,7 +58,10 @@ export default { rel: "noopener", }, { - href: "mailto:nbc-support@netz-21.de?subject=Niedersächsische%20Bildungscloud%20Anfrage", + href: + "mailto:" + + envConfigModule.getSupportProblemEmail + + "?subject=Niedersächsische%20Bildungscloud%20Anfrage", text: this.$t("components.legacy.footer.contact"), }, ]; diff --git a/src/themes/n21/components/templates/impressum.unit.ts b/src/themes/n21/components/templates/impressum.unit.ts new file mode 100644 index 0000000000..a41b917644 --- /dev/null +++ b/src/themes/n21/components/templates/impressum.unit.ts @@ -0,0 +1,55 @@ +import { envConfigModule } from "@/store"; +import EnvConfigModule from "@/store/env-config"; +import setupStores from "@@/tests/test-utils/setupStores"; +import Impressum from "./impressum.vue"; +import { envsFactory } from "@@/tests/test-utils"; +import { createTestingI18n } from "@@/tests/test-utils/setup"; + +describe("impressum.vue", () => { + beforeEach(() => { + setupStores({ + envConfigModule: EnvConfigModule, + }); + }); + + const setup = (mail = "") => { + const envs = envsFactory.build({ + SUPPORT_PROBLEM_EMAIL_ADDRESS: mail, + }); + envConfigModule.setEnvs(envs); + + const wrapper = mount(Impressum, { + global: { + plugins: [createTestingI18n()], + }, + }); + return wrapper; + }; + + it("renders the component", () => { + const wrapper = setup(); + expect(wrapper.findComponent(Impressum).exists()).toBeTruthy(); + }); + + describe("when environment variable is no valid email", () => { + it("should use correct values fro supportMail and mailtoSupportMail", () => { + const supportMail = "support@example.com"; + const wrapper = setup(supportMail); + + const mailtoSupportMail = `mailto:${supportMail}`; + const supportMailLink = wrapper.find("[data-testid=support-mail]"); + expect(supportMailLink.attributes("href")).toBe(mailtoSupportMail); + expect(supportMailLink.text()).toBe(supportMail); + }); + }); + + describe("when environment variable is no valid email", () => { + it("should not use environment variable", () => { + const wrapper = setup("invalid-email"); + + const supportMailLink = wrapper.find("[data-testid=support-mail]"); + expect(supportMailLink.attributes("href")).toBe("#"); + expect(supportMailLink.text()).toBe(""); + }); + }); +}); diff --git a/src/themes/n21/components/templates/impressum.vue b/src/themes/n21/components/templates/impressum.vue index 5002e5e8ae..45a28f5568 100644 --- a/src/themes/n21/components/templates/impressum.vue +++ b/src/themes/n21/components/templates/impressum.vue @@ -22,8 +22,9 @@

Support-Anfragen richten Sie bitte direkt an - nbc-support@netz-21.de{{ + supportMail + }}.

Vertretungsberechtigter Geschäftsführer

@@ -89,8 +90,20 @@ -