From e979bb142ccbaa256502a89693d4d824978bd763 Mon Sep 17 00:00:00 2001 From: Afzal Khan Date: Thu, 22 Feb 2024 21:09:18 +0530 Subject: [PATCH] Fix part of #19435: Learner group editor page migration (#19761) * Fix part of #19435: Migrate learner group editor page * complete migration * remove browser module * complete migration * fix linter * fix linter * added frontend test * add test * fix linter * fix test * fix linter * fix linter * fix root * fix linter * update comment * removed use of context service and fixed typo * fix test * fix test * fix test * fix test * fix test * remove max-len flag * use paramMap instead of windowRef * avoid using hard coded string * add script for passive event --- assets/constants.ts | 18 +++ core/controllers/access_validators.py | 47 ++++++++ core/controllers/access_validators_test.py | 67 +++++++++++ core/controllers/learner_group.py | 38 ------ core/controllers/learner_group_test.py | 64 ----------- ...edit-learner-group-page-auth.guard.spec.ts | 108 ++++++++++++++++++ .../edit-learner-group-page-auth.guard.ts | 65 +++++++++++ ...dit-learner-group-page-root.component.html | 19 +++ ...-learner-group-page-root.component.spec.ts | 50 ++++++++ .../edit-learner-group-page-root.component.ts | 34 ++++++ .../edit-learner-group-page.component.ts | 5 - .../edit-learner-group-page.mainpage.html | 38 ------ .../edit-learner-group-page.module.ts | 86 +++----------- ...arner-group-learners-progress.component.ts | 5 - .../learner-group-preferences.component.ts | 5 - .../learner-group-syllabus.component.ts | 5 - ...ess-validation-backend-api.service.spec.ts | 16 +++ .../access-validation-backend-api.service.ts | 15 +++ .../oppia-root/routing/app.routing.module.ts | 8 ++ .../services/angular-services.index.ts | 2 + main.py | 8 +- webpack.common.config.ts | 14 --- 22 files changed, 472 insertions(+), 245 deletions(-) create mode 100644 core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page-auth.guard.spec.ts create mode 100644 core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page-auth.guard.ts create mode 100644 core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page-root.component.html create mode 100644 core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page-root.component.spec.ts create mode 100644 core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page-root.component.ts delete mode 100644 core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page.mainpage.html diff --git a/assets/constants.ts b/assets/constants.ts index 66ecb153a5c5..cf0664e6fbc0 100644 --- a/assets/constants.ts +++ b/assets/constants.ts @@ -7214,6 +7214,24 @@ export default { } ] }, + "LEARNER_GROUP_EDITOR": { + "ROUTE": "edit-learner-group/:learner_group_id", + "TITLE": "Edit Learner Group | Oppia", + "META": [ + { + "PROPERTY_TYPE": "itemprop", + "PROPERTY_VALUE": "description", + // eslint-disable-next-line max-len + "CONTENT": "With Oppia, you can access free lessons on math, physics, statistics, chemistry, music, history and more from anywhere in the world. Oppia is a nonprofit with the mission of providing high-quality education to those who lack access to it." + }, + { + "PROPERTY_TYPE": "itemprop", + "PROPERTY_VALUE": "og:description", + // eslint-disable-next-line max-len + "CONTENT": "With Oppia, you can access free lessons on math, physics, statistics, chemistry, music, history and more from anywhere in the world. Oppia is a nonprofit with the mission of providing high-quality education to those who lack access to it." + } + ] + }, "LEARNER_GROUP_VIEWER": { "ROUTE": "learner-group/:learner_group_id", "TITLE": "I18N_LEARNER_GROUP_PAGE_TITLE", diff --git a/core/controllers/access_validators.py b/core/controllers/access_validators.py index 865a78943cbd..093b8836ccdd 100644 --- a/core/controllers/access_validators.py +++ b/core/controllers/access_validators.py @@ -196,6 +196,53 @@ def get(self, learner_group_id: str) -> None: raise self.PageNotFoundException +class EditLearnerGroupPageAccessValidationHandler( + base.BaseHandler[Dict[str, str], Dict[str, str]] +): + """Validates access to edit learner group page.""" + + GET_HANDLER_ERROR_RETURN_TYPE = feconf.HANDLER_TYPE_JSON + + URL_PATH_ARGS_SCHEMAS = { + 'learner_group_id': { + 'schema': { + 'type': 'basestring', + 'validators': [{ + 'id': 'is_regex_matched', + 'regex_pattern': constants.LEARNER_GROUP_ID_REGEX + }] + } + } + } + HANDLER_ARGS_SCHEMAS: Dict[str, Dict[str, str]] = { + 'GET': {} + } + + @acl_decorators.can_access_learner_groups + def get(self, learner_group_id: str) -> None: + """Validates access to edit learner group page. + + Args: + learner_group_id: str. The learner group ID. + + Raises: + PageNotFoundException. The learner groups are not enabled. + PageNotFoundException. The user is not a member of the learner + group. + """ + assert self.user_id is not None + if not learner_group_services.is_learner_group_feature_enabled( + self.user_id + ): + raise self.PageNotFoundException + + is_valid_request = learner_group_services.is_user_facilitator( + self.user_id, learner_group_id) + + if not is_valid_request: + raise self.PageNotFoundException + + class BlogHomePageAccessValidationHandler( base.BaseHandler[Dict[str, str], Dict[str, str]] ): diff --git a/core/controllers/access_validators_test.py b/core/controllers/access_validators_test.py index 582654b717e7..db17d976c936 100644 --- a/core/controllers/access_validators_test.py +++ b/core/controllers/access_validators_test.py @@ -255,6 +255,73 @@ def test_validation_returns_true_for_valid_learner(self) -> None: ACCESS_VALIDATION_HANDLER_PREFIX, self.LEARNER_GROUP_ID)) +class EditLearnerGroupPageAccessValidationHandlerTests( + test_utils.GenericTestBase +): + + def setUp(self) -> None: + super().setUp() + self.signup(self.NEW_USER_EMAIL, self.NEW_USER_USERNAME) + self.signup( + self.CURRICULUM_ADMIN_EMAIL, self.CURRICULUM_ADMIN_USERNAME) + + self.facilitator_id = self.get_user_id_from_email( + self.CURRICULUM_ADMIN_EMAIL) + + self.LEARNER_GROUP_ID = ( + learner_group_fetchers.get_new_learner_group_id() + ) + learner_group_services.create_learner_group( + self.LEARNER_GROUP_ID, 'Learner Group Title', 'Description', + [self.facilitator_id], [], + ['subtopic_id_1'], ['story_id_1']) + + def test_validation_returns_false_with_learner_groups_feature_disabled( + self + ) -> None: + self.login(self.CURRICULUM_ADMIN_EMAIL) + + swap_is_feature_flag_enabled = self.swap_to_always_return( + feature_flag_services, + 'is_feature_flag_enabled', + False + ) + with swap_is_feature_flag_enabled: + self.get_json( + '%s/can_access_edit_learner_group_page/%s' % ( + ACCESS_VALIDATION_HANDLER_PREFIX, self.LEARNER_GROUP_ID), + expected_status_int=404) + + def test_validation_returns_false_with_user_not_being_a_facilitator( + self + ) -> None: + self.login(self.NEW_USER_EMAIL) + + swap_is_feature_flag_enabled = self.swap_to_always_return( + feature_flag_services, + 'is_feature_flag_enabled', + True + ) + with swap_is_feature_flag_enabled: + self.get_json( + '%s/can_access_edit_learner_group_page/%s' % ( + ACCESS_VALIDATION_HANDLER_PREFIX, self.LEARNER_GROUP_ID), + expected_status_int=404) + + def test_validation_returns_true_for_valid_facilitator(self) -> None: + self.login(self.CURRICULUM_ADMIN_EMAIL) + + swap_is_feature_flag_enabled = self.swap_to_always_return( + feature_flag_services, + 'is_feature_flag_enabled', + True + ) + with swap_is_feature_flag_enabled: + self.get_html_response( + '%s/can_access_edit_learner_group_page/%s' % ( + ACCESS_VALIDATION_HANDLER_PREFIX, self.LEARNER_GROUP_ID)) + + class BlogHomePageAccessValidationHandlerTests(test_utils.GenericTestBase): """Checks the access to the blog home page and its rendering.""" diff --git a/core/controllers/learner_group.py b/core/controllers/learner_group.py index 394291638d33..0c628e1cabba 100644 --- a/core/controllers/learner_group.py +++ b/core/controllers/learner_group.py @@ -761,44 +761,6 @@ def get(self) -> None: }) -class EditLearnerGroupPage( - base.BaseHandler[Dict[str, str], Dict[str, str]] -): - """Page for editing a learner group.""" - - URL_PATH_ARGS_SCHEMAS = { - 'group_id': { - 'schema': { - 'type': 'basestring', - 'validators': [{ - 'id': 'is_regex_matched', - 'regex_pattern': constants.LEARNER_GROUP_ID_REGEX - }] - } - } - } - HANDLER_ARGS_SCHEMAS: Dict[str, Dict[str, str]] = { - 'GET': {} - } - - @acl_decorators.can_access_learner_groups - def get(self, group_id: str) -> None: - """Handles GET requests.""" - assert self.user_id is not None - if not learner_group_services.is_learner_group_feature_enabled( - self.user_id - ): - raise self.PageNotFoundException - - is_valid_request = learner_group_services.is_user_facilitator( - self.user_id, group_id) - - if not is_valid_request: - raise self.PageNotFoundException - - self.render_template('edit-learner-group-page.mainpage.html') - - class LearnerGroupLearnersInfoHandler( base.BaseHandler[Dict[str, str], Dict[str, str]] ): diff --git a/core/controllers/learner_group_test.py b/core/controllers/learner_group_test.py index 93c626ae1374..d1959a831083 100644 --- a/core/controllers/learner_group_test.py +++ b/core/controllers/learner_group_test.py @@ -1007,70 +1007,6 @@ def test_searching_a_valid_user_to_invite(self) -> None: self.logout() -class EditLearnerGroupPageTests(test_utils.GenericTestBase): - """Checks the access and rendering of the edit learner page.""" - - LEARNER_ID: Final = 'learner_user_1' - - def setUp(self) -> None: - super().setUp() - self.signup(self.OWNER_EMAIL, self.OWNER_USERNAME) - self.FACILITATOR_ID = self.get_user_id_from_email(self.OWNER_EMAIL) - self.signup(self.NEW_USER_EMAIL, self.NEW_USER_USERNAME) - self.login(self.OWNER_EMAIL) - self.learner_group_id = ( - learner_group_fetchers.get_new_learner_group_id() - ) - self.learner_group = learner_group_services.create_learner_group( - self.learner_group_id, 'Learner Group Name', 'Description', - [self.FACILITATOR_ID], [self.LEARNER_ID], ['subtopic_id_1'], - ['story_id_1']) - - def test_page_with_disabled_learner_groups_leads_to_404(self) -> None: - swap_is_feature_flag_enabled = self.swap_to_always_return( - feature_flag_services, - 'is_feature_flag_enabled', - False - ) - with swap_is_feature_flag_enabled: - self.get_html_response( - '/edit-learner-group/%s' % self.learner_group_id, - expected_status_int=404) - self.logout() - - def test_page_with_enabled_learner_groups_loads_correctly_for_facilitator( - self - ) -> None: - swap_is_feature_flag_enabled = self.swap_to_always_return( - feature_flag_services, - 'is_feature_flag_enabled', - True - ) - with swap_is_feature_flag_enabled: - response = self.get_html_response( - '/edit-learner-group/%s' % self.learner_group_id) - response.mustcontain( - '' - '') - self.logout() - - def test_page_with_enabled_learner_groups_leads_to_404_for_non_facilitators( - self - ) -> None: - swap_is_feature_flag_enabled = self.swap_to_always_return( - feature_flag_services, - 'is_feature_flag_enabled', - True - ) - self.logout() - self.login(self.NEW_USER_EMAIL) - with swap_is_feature_flag_enabled: - self.get_html_response( - '/edit-learner-group/%s' % self.learner_group_id, - expected_status_int=404) - self.logout() - - class LearnerGroupLearnerInvitationHandlerTests(test_utils.GenericTestBase): """Checks learner successfully accepting or declining a learner group invitation. diff --git a/core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page-auth.guard.spec.ts b/core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page-auth.guard.spec.ts new file mode 100644 index 000000000000..41c60fae77b4 --- /dev/null +++ b/core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page-auth.guard.spec.ts @@ -0,0 +1,108 @@ +// Copyright 2023 The Oppia Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Tests for EditLearnerGroupPageAuthGuard + */ +import { Location } from '@angular/common'; +import { TestBed, fakeAsync, tick } from '@angular/core/testing'; +import { ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { AppConstants } from 'app.constants'; +import { EditLearnerGroupPageAuthGuard } from './edit-learner-group-page-auth.guard'; +import { AccessValidationBackendApiService } from 'pages/oppia-root/routing/access-validation-backend-api.service'; + + +class MockAccessValidationBackendApiService { + validateAccessToLearnerGroupEditorPage(learnerGroupId: string) { + return Promise.resolve(); + } +} + +class MockRouter { + navigate(commands: string[]): Promise { + return Promise.resolve(true); + } +} + +describe('EditLearnerGroupPageAuthGuard', () => { + let guard: EditLearnerGroupPageAuthGuard; + let accessValidationBackendApiService: AccessValidationBackendApiService; + let router: Router; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule], + providers: [ + EditLearnerGroupPageAuthGuard, + { provide: AccessValidationBackendApiService, + useClass: MockAccessValidationBackendApiService }, + { provide: Router, useClass: MockRouter }, + Location, + ], + }); + + guard = TestBed.inject(EditLearnerGroupPageAuthGuard); + accessValidationBackendApiService = TestBed.inject( + AccessValidationBackendApiService + ); + router = TestBed.inject(Router); + }); + + it('should allow access if validation succeeds', fakeAsync(() => { + const validateAccessSpy = spyOn( + accessValidationBackendApiService, + 'validateAccessToLearnerGroupEditorPage') + .and.returnValue(Promise.resolve()); + const navigateSpy = spyOn(router, 'navigate') + .and.returnValue(Promise.resolve(true)); + + let canActivateResult: boolean | null = null; + + guard.canActivate(new ActivatedRouteSnapshot(), {} as RouterStateSnapshot) + .then((result) => { + canActivateResult = result; + }); + + tick(); + + expect(canActivateResult).toBeTrue(); + expect(validateAccessSpy).toHaveBeenCalled(); + expect(navigateSpy).not.toHaveBeenCalled(); + })); + + it('should redirect to 401 page if validation fails', fakeAsync(() => { + spyOn( + accessValidationBackendApiService, + 'validateAccessToLearnerGroupEditorPage') + .and.returnValue(Promise.reject()); + const navigateSpy = spyOn(router, 'navigate') + .and.returnValue(Promise.resolve(true)); + + let canActivateResult: boolean | null = null; + + guard.canActivate(new ActivatedRouteSnapshot(), {} as RouterStateSnapshot) + .then((result) => { + canActivateResult = result; + }); + + tick(); + + expect(canActivateResult).toBeFalse(); + expect(navigateSpy).toHaveBeenCalledWith( + [`${AppConstants.PAGES_REGISTERED_WITH_FRONTEND.ERROR.ROUTE}/401`] + ); + })); +}); diff --git a/core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page-auth.guard.ts b/core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page-auth.guard.ts new file mode 100644 index 000000000000..db24a07664b2 --- /dev/null +++ b/core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page-auth.guard.ts @@ -0,0 +1,65 @@ +// Copyright 2023 The Oppia Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Guard that redirects user to 401 error page + * if the user is not a valid facilitator. + */ + +import { Location } from '@angular/common'; +import { Injectable } from '@angular/core'; +import { + ActivatedRouteSnapshot, + CanActivate, + Router, + RouterStateSnapshot, +} from '@angular/router'; + +import { AppConstants } from 'app.constants'; +import { AccessValidationBackendApiService } from 'pages/oppia-root/routing/access-validation-backend-api.service'; + +@Injectable({ + providedIn: 'root' +}) +export class EditLearnerGroupPageAuthGuard implements CanActivate { + constructor( + private accessValidationBackendApiService: + AccessValidationBackendApiService, + private router: Router, + private location: Location + ) {} + + async canActivate( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): Promise { + let learnerGroupId = route.paramMap.get('learner_group_id') || ''; + + return new Promise((resolve) => { + this.accessValidationBackendApiService + .validateAccessToLearnerGroupEditorPage(learnerGroupId) + .then(() => { + resolve(true); + }) + .catch((err) => { + this.router.navigate( + [`${AppConstants.PAGES_REGISTERED_WITH_FRONTEND.ERROR.ROUTE}/401`]) + .then(() => { + this.location.replaceState(state.url); + resolve(false); + }); + }); + }); + } +} diff --git a/core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page-root.component.html b/core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page-root.component.html new file mode 100644 index 000000000000..6baf473bd464 --- /dev/null +++ b/core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page-root.component.html @@ -0,0 +1,19 @@ + + + + + + + + + + + + diff --git a/core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page-root.component.spec.ts b/core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page-root.component.spec.ts new file mode 100644 index 000000000000..3fd62d19fafb --- /dev/null +++ b/core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page-root.component.spec.ts @@ -0,0 +1,50 @@ +// Copyright 2024 The Oppia Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Unit tests for Learner Group Editor Root component. + */ + +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { TranslateModule } from '@ngx-translate/core'; + +import { AppConstants } from '../../../app.constants'; +import { PageHeadService } from '../../../services/page-head.service'; +import { EditLearnerGroupPageRootComponent } from './edit-learner-group-page-root.component'; + +describe('EditLearnerGroupPageRootComponent', () => { + let fixture: ComponentFixture; + let component: EditLearnerGroupPageRootComponent; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot(), HttpClientTestingModule], + declarations: [EditLearnerGroupPageRootComponent], + providers: [PageHeadService], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + }).compileComponents(); + + fixture = TestBed.createComponent(EditLearnerGroupPageRootComponent); + component = fixture.componentInstance; + }); + + it('should have the title and meta tags set', () => { + expect(component.title).toEqual( + AppConstants.PAGES_REGISTERED_WITH_FRONTEND.LEARNER_GROUP_EDITOR.TITLE); + expect(component.meta).toEqual( + AppConstants.PAGES_REGISTERED_WITH_FRONTEND.LEARNER_GROUP_EDITOR.META); + }); +}); diff --git a/core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page-root.component.ts b/core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page-root.component.ts new file mode 100644 index 000000000000..f30dc0243625 --- /dev/null +++ b/core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page-root.component.ts @@ -0,0 +1,34 @@ +// Copyright 2023 The Oppia Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Learner Group Editor page root component. + */ + +import { Component } from '@angular/core'; +import { AppConstants } from 'app.constants'; +import { BaseRootComponent, MetaTagData } from 'pages/base-root.component'; + +@Component({ + selector: 'oppia-edit-learner-group-page-root', + templateUrl: './edit-learner-group-page-root.component.html', +}) +export class EditLearnerGroupPageRootComponent extends BaseRootComponent { + title: string = AppConstants. + PAGES_REGISTERED_WITH_FRONTEND.LEARNER_GROUP_EDITOR.TITLE; + + meta: MetaTagData[] = + AppConstants.PAGES_REGISTERED_WITH_FRONTEND.LEARNER_GROUP_EDITOR.META as + unknown as Readonly[]; +} diff --git a/core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page.component.ts b/core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page.component.ts index 5d28b6a6ac5c..54ecfb45390c 100644 --- a/core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page.component.ts +++ b/core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page.component.ts @@ -17,7 +17,6 @@ */ import { Component, OnDestroy, OnInit } from '@angular/core'; -import { downgradeComponent } from '@angular/upgrade/static'; import { TranslateService } from '@ngx-translate/core'; import { Subscription } from 'rxjs'; @@ -99,7 +98,3 @@ export class EditLearnerGroupPageComponent implements OnInit, OnDestroy { this.directiveSubscriptions.unsubscribe(); } } - -angular.module('oppia').directive( - 'oppiaEditLearnerGroupPage', - downgradeComponent({component: EditLearnerGroupPageComponent})); diff --git a/core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page.mainpage.html b/core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page.mainpage.html deleted file mode 100644 index c1e374e4725a..000000000000 --- a/core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page.mainpage.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - @load('base-components/header.template.html', {"title": "Edit Learner Group | Oppia"}) - - - - -
- - - - - - - - -
- -
- @load('pages/footer_js_libs.html') - - - diff --git a/core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page.module.ts b/core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page.module.ts index 0b9e22c4a2c6..b2b277db1f45 100644 --- a/core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page.module.ts +++ b/core/templates/pages/learner-group-pages/edit-group/edit-learner-group-page.module.ts @@ -16,23 +16,19 @@ * @fileoverview Module for the edit learner group page. */ -import { APP_INITIALIZER, NgModule, StaticProvider } from '@angular/core'; -import { BrowserModule, HAMMER_GESTURE_CONFIG } from - '@angular/platform-browser'; -import { downgradeComponent } from '@angular/upgrade/static'; -import { HttpClientModule } from '@angular/common/http'; -import { HTTP_INTERCEPTORS } from '@angular/common/http'; -import { RequestInterceptor } from 'services/request-interceptor.service'; +import { NgModule } from '@angular/core'; import { SharedComponentsModule } from 'components/shared-component.module'; +import { ToastrModule } from 'ngx-toastr'; +import { toastrConfig } from 'pages/oppia-root/app.module'; import { RouterModule } from '@angular/router'; -import { APP_BASE_HREF } from '@angular/common'; +import { CommonModule } from '@angular/common'; +import { EditLearnerGroupPageAuthGuard } from './edit-learner-group-page-auth.guard'; +import { EditLearnerGroupPageRootComponent } from './edit-learner-group-page-root.component'; import { EditLearnerGroupPageComponent } from './edit-learner-group-page.component'; import { LearnerGroupSyllabusComponent } from './learner-group-syllabus.component'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { MyHammerConfig, toastrConfig } from 'pages/oppia-root/app.module'; import { RemoveItemModalComponent } from '../templates/remove-item-modal.component'; @@ -54,18 +50,20 @@ import { SharedLearnerGroupComponentsModule } from 'pages/learner-group-pages/sh @NgModule({ imports: [ - BrowserModule, - BrowserAnimationsModule, - HttpClientModule, - // TODO(#13443): Remove smart router module provider once all pages are - // migrated to angular router. - SmartRouterModule, - RouterModule.forRoot([]), SharedComponentsModule, + CommonModule, SharedLearnerGroupComponentsModule, - ToastrModule.forRoot(toastrConfig) + ToastrModule.forRoot(toastrConfig), + RouterModule.forChild([ + { + path: '', + component: EditLearnerGroupPageRootComponent, + canActivate: [EditLearnerGroupPageAuthGuard], + }, + ]), ], declarations: [ + EditLearnerGroupPageRootComponent, EditLearnerGroupPageComponent, LearnerGroupSyllabusComponent, LearnerGroupLearnersProgressComponent, @@ -87,56 +85,6 @@ import { SharedLearnerGroupComponentsModule } from 'pages/learner-group-pages/sh InviteSuccessfulModalComponent, DeleteLearnerGroupModalComponent ], - providers: [ - { - provide: HTTP_INTERCEPTORS, - useClass: RequestInterceptor, - multi: true - }, - { - provide: APP_INITIALIZER, - useFactory: platformFeatureInitFactory, - deps: [PlatformFeatureService], - multi: true - }, - { - provide: HAMMER_GESTURE_CONFIG, - useClass: MyHammerConfig - }, - { - provide: APP_BASE_HREF, - useValue: '/' - } - ] }) -class EditLearnerGroupPageModule { - // Empty placeholder method to satisfy the `Compiler`. - ngDoBootstrap() {} -} - -import { platformFeatureInitFactory, PlatformFeatureService } from - 'services/platform-feature.service'; -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; -import { downgradeModule } from '@angular/upgrade/static'; -import { ToastrModule } from 'ngx-toastr'; -import { SmartRouterModule } from 'hybrid-router-module-provider'; -import { OppiaAngularRootComponent } from - 'components/oppia-angular-root.component'; - -const bootstrapFnAsync = async(extraProviders: StaticProvider[]) => { - const platformRef = platformBrowserDynamic(extraProviders); - return platformRef.bootstrapModule(EditLearnerGroupPageModule); -}; -const downgradedModule = downgradeModule(bootstrapFnAsync); - -declare var angular: ng.IAngularStatic; - -angular.module('oppia').requires.push(downgradedModule); -angular.module('oppia').directive( - // This directive is the downgraded version of the Angular component to - // bootstrap the Angular 8. - 'oppiaAngularRoot', - downgradeComponent({ - component: OppiaAngularRootComponent - }) as angular.IDirectiveFactory); +export class EditLearnerGroupPageModule {} diff --git a/core/templates/pages/learner-group-pages/edit-group/learner-group-learners-progress.component.ts b/core/templates/pages/learner-group-pages/edit-group/learner-group-learners-progress.component.ts index 02a30e6a9de1..89beb4ad9e7e 100644 --- a/core/templates/pages/learner-group-pages/edit-group/learner-group-learners-progress.component.ts +++ b/core/templates/pages/learner-group-pages/edit-group/learner-group-learners-progress.component.ts @@ -17,7 +17,6 @@ */ import { Component, Input, OnInit } from '@angular/core'; -import { downgradeComponent } from '@angular/upgrade/static'; import { ChapterProgressSummary } from 'domain/exploration/chapter-progress-summary.model'; import { LearnerGroupSyllabusBackendApiService } from 'domain/learner_group/learner-group-syllabus-backend-api.service'; import { LearnerGroupUserProgress } from 'domain/learner_group/learner-group-user-progress.model'; @@ -155,7 +154,3 @@ export class LearnerGroupLearnersProgressComponent implements OnInit { this.navigationService.openSubmenu(evt, menuName); } } - -angular.module('oppia').directive( - 'oppiaLearnerGroupLearnersProgress', - downgradeComponent({component: LearnerGroupLearnersProgressComponent})); diff --git a/core/templates/pages/learner-group-pages/edit-group/learner-group-preferences.component.ts b/core/templates/pages/learner-group-pages/edit-group/learner-group-preferences.component.ts index 07295b4033e9..203638531def 100644 --- a/core/templates/pages/learner-group-pages/edit-group/learner-group-preferences.component.ts +++ b/core/templates/pages/learner-group-pages/edit-group/learner-group-preferences.component.ts @@ -17,7 +17,6 @@ */ import { Component, Input, OnInit } from '@angular/core'; -import { downgradeComponent } from '@angular/upgrade/static'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { LearnerGroupBackendApiService } from 'domain/learner_group/learner-group-backend-api.service'; import { LearnerGroupUserInfo } from 'domain/learner_group/learner-group-user-info.model'; @@ -250,7 +249,3 @@ export class LearnerGroupPreferencesComponent implements OnInit { }); } } - -angular.module('oppia').directive( - 'oppiaLearnerGroupPreferences', - downgradeComponent({component: LearnerGroupPreferencesComponent})); diff --git a/core/templates/pages/learner-group-pages/edit-group/learner-group-syllabus.component.ts b/core/templates/pages/learner-group-pages/edit-group/learner-group-syllabus.component.ts index 0513428ccd20..88c7c08ac98e 100644 --- a/core/templates/pages/learner-group-pages/edit-group/learner-group-syllabus.component.ts +++ b/core/templates/pages/learner-group-pages/edit-group/learner-group-syllabus.component.ts @@ -17,7 +17,6 @@ */ import { Component, Input, OnInit } from '@angular/core'; -import { downgradeComponent } from '@angular/upgrade/static'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { AppConstants } from 'app.constants'; import { LearnerGroupBackendApiService } from 'domain/learner_group/learner-group-backend-api.service'; @@ -257,7 +256,3 @@ export class LearnerGroupSyllabusComponent implements OnInit { }); } } - -angular.module('oppia').directive( - 'oppiaLearnerGroupSyllabus', - downgradeComponent({component: LearnerGroupSyllabusComponent})); diff --git a/core/templates/pages/oppia-root/routing/access-validation-backend-api.service.spec.ts b/core/templates/pages/oppia-root/routing/access-validation-backend-api.service.spec.ts index 223d02102815..f574903a87b6 100644 --- a/core/templates/pages/oppia-root/routing/access-validation-backend-api.service.spec.ts +++ b/core/templates/pages/oppia-root/routing/access-validation-backend-api.service.spec.ts @@ -112,6 +112,22 @@ describe('Access validation backend api service', () => { expect(failSpy).not.toHaveBeenCalled(); })); + it('should validate access to learner group editor page', fakeAsync(() => { + let learnerGroupId = 'test_id'; + + avbas.validateAccessToLearnerGroupEditorPage(learnerGroupId) + .then(successSpy, failSpy); + + const req = httpTestingController.expectOne( + '/access_validation_handler/can_access_edit_learner_group_page/test_id'); + expect(req.request.method).toEqual('GET'); + req.flush({}); + + flushMicrotasks(); + expect(successSpy).toHaveBeenCalled(); + expect(failSpy).not.toHaveBeenCalled(); + })); + it('should validate whether given learner group exists', fakeAsync(() => { let learnerGroupId = 'groupId'; diff --git a/core/templates/pages/oppia-root/routing/access-validation-backend-api.service.ts b/core/templates/pages/oppia-root/routing/access-validation-backend-api.service.ts index 5ff480e8e2af..6947208e7baf 100644 --- a/core/templates/pages/oppia-root/routing/access-validation-backend-api.service.ts +++ b/core/templates/pages/oppia-root/routing/access-validation-backend-api.service.ts @@ -36,6 +36,11 @@ export class AccessValidationBackendApiService { RELEASE_COORDINATOR_PAGE_ACCESS_VALIDATOR = ( '/access_validation_handler/can_access_release_coordinator_page'); + LEARNER_GROUP_EDITOR_PAGE_ACCESS_VALIDATOR = ( + '/access_validation_handler/can_access_edit_learner_group_page/' + + '' + ); + DOES_LEARNER_GROUP_EXIST = ( '/access_validation_handler/does_learner_group_exist/'); @@ -108,6 +113,16 @@ export class AccessValidationBackendApiService { this.RELEASE_COORDINATOR_PAGE_ACCESS_VALIDATOR).toPromise(); } + validateAccessToLearnerGroupEditorPage( + learnerGroupId: string): Promise { + let url = this.urlInterpolationService.interpolateUrl( + this.LEARNER_GROUP_EDITOR_PAGE_ACCESS_VALIDATOR, { + learner_group_id: learnerGroupId + }); + + return this.http.get(url).toPromise(); + } + doesLearnerGroupExist(learnerGroupId: string): Promise { let url = this.urlInterpolationService.interpolateUrl( this.DOES_LEARNER_GROUP_EXIST, { diff --git a/core/templates/pages/oppia-root/routing/app.routing.module.ts b/core/templates/pages/oppia-root/routing/app.routing.module.ts index f8fb6ed86eb7..370f6fd8773c 100644 --- a/core/templates/pages/oppia-root/routing/app.routing.module.ts +++ b/core/templates/pages/oppia-root/routing/app.routing.module.ts @@ -79,6 +79,14 @@ const routes: Route[] = [ .then(m => m.LearnerDashboardPageModule), canActivate: [IsLoggedInGuard] }, + { + path: AppConstants.PAGES_REGISTERED_WITH_FRONTEND.LEARNER_GROUP_EDITOR + .ROUTE, + loadChildren: () => import( + 'pages/learner-group-pages/edit-group/edit-learner-group-page.module') + .then(m => m.EditLearnerGroupPageModule), + canActivate: [IsLoggedInGuard] + }, { path: AppConstants.PAGES_REGISTERED_WITH_FRONTEND.ABOUT.ROUTE, loadChildren: () => import('pages/about-page/about-page.module') diff --git a/core/templates/services/angular-services.index.ts b/core/templates/services/angular-services.index.ts index f77cdc1bac88..225863161cef 100644 --- a/core/templates/services/angular-services.index.ts +++ b/core/templates/services/angular-services.index.ts @@ -296,6 +296,7 @@ import { WindowRef } from 'services/contextual/window-ref.service'; import { CsrfTokenService } from 'services/csrf-token.service'; import { DateTimeFormatService } from 'services/date-time-format.service'; import { EditabilityService } from 'services/editability.service'; +import { EditLearnerGroupPageAuthGuard } from '../pages/learner-group-pages/edit-group/edit-learner-group-page-auth.guard'; import { ExplorationFeaturesBackendApiService } from 'services/exploration-features-backend-api.service'; import { ExplorationFeaturesService } from 'services/exploration-features.service'; import { ExplorationHtmlFormatterService } from 'services/exploration-html-formatter.service'; @@ -545,6 +546,7 @@ export const angularServices: [string, Type<{}>][] = [ ['DragAndDropSortInputValidationService', DragAndDropSortInputValidationService], ['EditabilityService', EditabilityService], + ['EditLearnerGroupPageAuthGuard', EditLearnerGroupPageAuthGuard], ['EditableCollectionBackendApiService', EditableCollectionBackendApiService], ['EditableExplorationBackendApiService', EditableExplorationBackendApiService], diff --git a/main.py b/main.py index 0628f0aea58e..d612262c1237 100644 --- a/main.py +++ b/main.py @@ -267,6 +267,12 @@ def get_redirect_route( access_validators.ReleaseCoordinatorAccessValidationHandler ), + get_redirect_route( + r'%s/can_access_edit_learner_group_page/' % + feconf.ACCESS_VALIDATION_HANDLER_PREFIX, + access_validators.EditLearnerGroupPageAccessValidationHandler + ), + get_redirect_route( r'%s/does_learner_group_exist/' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, @@ -1113,8 +1119,6 @@ def get_redirect_route( get_redirect_route( r'/exit_learner_group_handler/', learner_group.ExitLearnerGroupHandler), - get_redirect_route( - r'/edit-learner-group/', learner_group.EditLearnerGroupPage), get_redirect_route( r'/user_progress_in_stories_chapters_handler/', learner_group.LearnerStoriesChaptersProgressHandler), diff --git a/webpack.common.config.ts b/webpack.common.config.ts index 8416d0b310b5..714aad02c26d 100644 --- a/webpack.common.config.ts +++ b/webpack.common.config.ts @@ -93,9 +93,6 @@ module.exports = { learner_group_creator: commonPrefix + '/pages/learner-group-pages/create-group/' + 'create-learner-group-page.import.ts', - learner_group_editor: - commonPrefix + '/pages/learner-group-pages/edit-group/' + - 'edit-learner-group-page.import.ts', maintenance: commonPrefix + '/pages/maintenance-page/maintenance-page.import.ts', oppia_root: @@ -442,17 +439,6 @@ module.exports = { minify: htmlMinifyConfig, inject: false }), - new HtmlWebpackPlugin({ - chunks: ['learner_group_editor'], - filename: 'edit-learner-group-page.mainpage.html', - hybrid: true, - meta: defaultMeta, - template: - commonPrefix + '/pages/learner-group-pages/edit-group/' + - 'edit-learner-group-page.mainpage.html', - minify: htmlMinifyConfig, - inject: false - }), new HtmlWebpackPlugin({ chunks: ['voiceover_admin'], filename: 'voiceover-admin-page.mainpage.html',