From 94444fbc96c49e27eda3fbf7eec3bb222832acf3 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Wed, 20 Jul 2022 12:03:30 +0200 Subject: [PATCH 1/4] MOBILE-4034 login: Close IAB after user changes password --- .../pages/change-password/change-password.ts | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/core/features/login/pages/change-password/change-password.ts b/src/core/features/login/pages/change-password/change-password.ts index 1c0428e4e..697e5d939 100644 --- a/src/core/features/login/pages/change-password/change-password.ts +++ b/src/core/features/login/pages/change-password/change-password.ts @@ -12,13 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component } from '@angular/core'; +import { Component, OnDestroy } from '@angular/core'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreLoginHelper } from '@features/login/services/login-helper'; import { Translate } from '@singletons'; import { CoreNavigator } from '@services/navigator'; +import { CoreEventObserver, CoreEvents } from '@singletons/events'; +import { CoreUtils } from '@services/utils/utils'; /** * Page that shows instructions to change the password. @@ -27,11 +29,14 @@ import { CoreNavigator } from '@services/navigator'; selector: 'page-core-login-change-password', templateUrl: 'change-password.html', }) -export class CoreLoginChangePasswordPage { +export class CoreLoginChangePasswordPage implements OnDestroy { changingPassword = false; logoutLabel: string; + protected urlLoadedObserver?: CoreEventObserver; + protected browserClosedObserver?: CoreEventObserver; + constructor() { this.logoutLabel = CoreLoginHelper.getLogoutLabel(); } @@ -57,6 +62,7 @@ export class CoreLoginChangePasswordPage { true, ); this.changingPassword = true; + this.detectPasswordChanged(); } /** @@ -75,4 +81,37 @@ export class CoreLoginChangePasswordPage { this.changingPassword = false; } + /** + * Try to detect if the user changed password in browser. + */ + detectPasswordChanged(): void { + if (this.urlLoadedObserver) { + // Already listening (shouldn't happen). + return; + } + + this.urlLoadedObserver = CoreEvents.on(CoreEvents.IAB_LOAD_START, (event) => { + if (event.url.match(/\/login\/change_password\.php.*return=1/)) { + // Password should have changed. + CoreUtils.closeInAppBrowser(); + this.login(); + } + }); + + this.browserClosedObserver = CoreEvents.on(CoreEvents.IAB_EXIT, () => { + this.urlLoadedObserver?.off(); + this.browserClosedObserver?.off(); + delete this.urlLoadedObserver; + delete this.browserClosedObserver; + }); + } + + /** + * @inheritdoc + */ + ngOnDestroy(): void { + this.urlLoadedObserver?.off(); + this.browserClosedObserver?.off(); + } + } From c91e1192ae2c993db52dcfc2ec7914f1d9244dea Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Wed, 20 Jul 2022 14:44:07 +0200 Subject: [PATCH 2/4] MOBILE-4034 user: Improve UX when missing required fields --- scripts/langindex.json | 8 +- src/app/app.component.ts | 3 +- src/core/classes/site.ts | 2 + src/core/features/login/lang.json | 9 +- .../change-password/change-password.html | 6 +- src/core/features/user/lang.json | 4 + .../complete-profile/complete-profile.html | 50 ++++++++ .../complete-profile/complete-profile.ts | 118 ++++++++++++++++++ .../features/user/services/user-helper.ts | 21 ++++ .../features/user/user-app-lazy.module.ts | 37 ++++++ src/core/features/user/user.module.ts | 10 +- src/core/lang.json | 1 + 12 files changed, 257 insertions(+), 12 deletions(-) create mode 100644 src/core/features/user/pages/complete-profile/complete-profile.html create mode 100644 src/core/features/user/pages/complete-profile/complete-profile.ts create mode 100644 src/core/features/user/user-app-lazy.module.ts diff --git a/scripts/langindex.json b/scripts/langindex.json index 5a015724d..ae08079ec 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -1903,8 +1903,6 @@ "core.login.changepassword": "moodle", "core.login.changepasswordbutton": "local_moodlemobileapp", "core.login.changepasswordhelp": "local_moodlemobileapp", - "core.login.changepasswordinstructions": "local_moodlemobileapp", - "core.login.changepasswordlogoutinstructions": "local_moodlemobileapp", "core.login.changepasswordreconnectinstructions": "local_moodlemobileapp", "core.login.confirmdeletesite": "local_moodlemobileapp", "core.login.connect": "local_moodlemobileapp", @@ -1993,6 +1991,7 @@ "core.login.recaptchaincorrect": "local_moodlemobileapp", "core.login.reconnect": "local_moodlemobileapp", "core.login.reconnectssodescription": "local_moodlemobileapp", + "core.login.reconnecttosite": "local_moodlemobileapp", "core.login.removeaccount": "local_moodlemobileapp", "core.login.resendemail": "moodle", "core.login.searchby": "local_moodlemobileapp", @@ -2334,6 +2333,10 @@ "core.user": "moodle", "core.user.address": "moodle", "core.user.city": "moodle", + "core.user.completeprofile": "local_moodlemobileapp", + "core.user.completeprofilenotice": "local_moodlemobileapp", + "core.user.completeprofilereconnectinstructions": "local_moodlemobileapp", + "core.user.completeyourprofile": "local_moodlemobileapp", "core.user.contact": "local_moodlemobileapp", "core.user.country": "moodle", "core.user.description": "moodle", @@ -2371,6 +2374,7 @@ "core.vieweditor": "local_moodlemobileapp", "core.viewembeddedcontent": "local_moodlemobileapp", "core.viewprofile": "moodle", + "core.wanttochangesite": "local_moodlemobileapp", "core.warningofflinedatadeleted": "local_moodlemobileapp", "core.warnopeninbrowser": "local_moodlemobileapp", "core.week": "moodle", diff --git a/src/app/app.component.ts b/src/app/app.component.ts index ec1d66cc4..5b75b9b9a 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -34,6 +34,7 @@ import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreDom } from '@singletons/dom'; import { CorePlatform } from '@services/platform'; +import { CoreUserHelper } from '@features/user/services/user-helper'; const MOODLE_VERSION_PREFIX = 'version-'; const MOODLEAPP_VERSION_PREFIX = 'moodleapp-'; @@ -110,7 +111,7 @@ export class AppComponent implements OnInit, AfterViewInit { CoreLoginHelper.passwordChangeForced(data.siteId!); }); CoreEvents.on(CoreEvents.USER_NOT_FULLY_SETUP, (data) => { - CoreLoginHelper.openInAppForEdit(data.siteId!, '/user/edit.php', 'core.usernotfullysetup'); + CoreUserHelper.openCompleteProfile(data.siteId!); }); // Listen for sitepolicynotagreed event to accept the site policy. diff --git a/src/core/classes/site.ts b/src/core/classes/site.ts index 5fcd79060..7580b54f3 100644 --- a/src/core/classes/site.ts +++ b/src/core/classes/site.ts @@ -675,10 +675,12 @@ export class CoreSite { // Password Change Forced, trigger event. Try to get data from cache, the event will handle the error. CoreEvents.trigger(CoreEvents.PASSWORD_CHANGE_FORCED, {}, this.id); error.message = Translate.instant('core.forcepasswordchangenotice'); + useSilentError = true; // Use a silent error, the change password page already displays the appropiate info. } else if (error.errorcode === 'usernotfullysetup') { // User not fully setup, trigger event. Try to get data from cache, the event will handle the error. CoreEvents.trigger(CoreEvents.USER_NOT_FULLY_SETUP, {}, this.id); error.message = Translate.instant('core.usernotfullysetup'); + useSilentError = true; // Use a silent error, the complete profile page already displays the appropiate info. } else if (error.errorcode === 'sitepolicynotagreed') { // Site policy not agreed, trigger event. CoreEvents.trigger(CoreEvents.SITE_POLICY_NOT_AGREED, {}, this.id); diff --git a/src/core/features/login/lang.json b/src/core/features/login/lang.json index 6eb0b227f..acbdb3cff 100644 --- a/src/core/features/login/lang.json +++ b/src/core/features/login/lang.json @@ -4,12 +4,10 @@ "auth_email": "Email-based self-registration", "authenticating": "Authenticating", "cancel": "Cancel", - "changepassword": "Change password", - "changepasswordbutton": "Open the change password page", + "changepassword": "Change your password", + "changepasswordbutton": "Change password", "changepasswordhelp": "If you have problems changing your password, please contact your site administrator. \"Site Administrators\" are the people who manages the Moodle at your school/university/company or learning organisation. If you don't know how to contact them, please contact your teachers/trainers.", - "changepasswordinstructions": "You cannot change your password in the app. Please click the following button to open the site in a web browser to change your password. Take into account you need to close the browser after changing the password as you will not be redirected to the app.", - "changepasswordlogoutinstructions": "If you prefer to change site or log out, please click the following button:", - "changepasswordreconnectinstructions": "Click the following button to reconnect to the site. (Take into account that if you didn't change your password successfully, you would return to the previous screen).", + "changepasswordreconnectinstructions": "If you didn't change your password correctly, you'll be asked to do it again.", "confirmdeletesite": "Are you sure you want to remove the account on {{sitename}}?", "connect": "Connect!", "connecttomoodle": "Connect to Moodle", @@ -97,6 +95,7 @@ "recaptchaincorrect": "The security question answer is incorrect.", "reconnect": "Reconnect", "reconnectssodescription": "Your authentication token is invalid or has expired. You have to reconnect to the site. You need to log in to the site in a browser window.", + "reconnecttosite": "Reconnect to the site", "removeaccount": "Remove account", "resendemail": "Resend email", "searchby": "Search by:", diff --git a/src/core/features/login/pages/change-password/change-password.html b/src/core/features/login/pages/change-password/change-password.html index 7b808cbd7..755a642f9 100644 --- a/src/core/features/login/pages/change-password/change-password.html +++ b/src/core/features/login/pages/change-password/change-password.html @@ -5,7 +5,8 @@ -

{{ 'core.login.changepassword' | translate }}

+

{{ 'core.login.changepassword' | translate }}

+

{{ 'core.login.reconnecttosite' | translate }}

@@ -21,7 +22,6 @@

{{ 'core.login.forcepasswordchangenotice' | translate }}

-

{{ 'core.login.changepasswordinstructions' | translate }}

@@ -40,7 +40,7 @@ -

{{ 'core.login.changepasswordlogoutinstructions' | translate }}

+

{{ 'core.wanttochangesite' | translate }}

diff --git a/src/core/features/user/lang.json b/src/core/features/user/lang.json index 978102d21..147b26797 100644 --- a/src/core/features/user/lang.json +++ b/src/core/features/user/lang.json @@ -2,6 +2,10 @@ "address": "Address", "useraccount": "User account", "city": "City/town", + "completeprofile": "Complete profile", + "completeprofilenotice": "Before you continue, please fill in the required fields in your user profile.", + "completeprofilereconnectinstructions": "If you didn't complete your profile correctly, you'll be asked to do it again.", + "completeyourprofile": "Complete your profile", "contact": "Contact", "country": "Country", "description": "Description", diff --git a/src/core/features/user/pages/complete-profile/complete-profile.html b/src/core/features/user/pages/complete-profile/complete-profile.html new file mode 100644 index 000000000..5c8910bd6 --- /dev/null +++ b/src/core/features/user/pages/complete-profile/complete-profile.html @@ -0,0 +1,50 @@ + + + + + + + +

{{ 'core.user.completeyourprofile' | translate }}

+

{{ 'core.login.reconnecttosite' | translate }}

+
+ + + + + + +
+
+ + + + + +

{{ 'core.user.completeprofilenotice' | translate }}

+
+
+ + {{ 'core.user.completeprofile' | translate }} + +
+ + + +

{{ 'core.user.completeprofilereconnectinstructions' | translate }}

+
+
+ + {{ 'core.login.reconnect' | translate }} + +
+ + +

{{ 'core.wanttochangesite' | translate }}

+
+
+ + {{ logoutLabel | translate }} + +
+
diff --git a/src/core/features/user/pages/complete-profile/complete-profile.ts b/src/core/features/user/pages/complete-profile/complete-profile.ts new file mode 100644 index 000000000..129be105d --- /dev/null +++ b/src/core/features/user/pages/complete-profile/complete-profile.ts @@ -0,0 +1,118 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// 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. + +import { Component, OnDestroy } from '@angular/core'; + +import { CoreSites } from '@services/sites'; +import { CoreDomUtils } from '@services/utils/dom'; +import { CoreLoginHelper } from '@features/login/services/login-helper'; +import { Translate } from '@singletons'; +import { CoreNavigator } from '@services/navigator'; +import { CoreEventObserver, CoreEvents } from '@singletons/events'; +import { CoreUtils } from '@services/utils/utils'; + +/** + * Page that shows instructions to complete the profile. + */ +@Component({ + selector: 'page-core-user-complete-profile', + templateUrl: 'complete-profile.html', +}) +export class CoreUserCompleteProfilePage implements OnDestroy { + + editingProfile = false; + logoutLabel: string; + + protected urlLoadedObserver?: CoreEventObserver; + protected browserClosedObserver?: CoreEventObserver; + + constructor() { + this.logoutLabel = CoreLoginHelper.getLogoutLabel(); + } + + /** + * Show a help modal. + */ + showHelp(): void { + // @todo MOBILE-4059: Change this message. + CoreDomUtils.showAlert( + Translate.instant('core.help'), + Translate.instant('core.login.changepasswordhelp'), + ); + } + + /** + * Open the edit profile page in a browser. + */ + openCompleteProfilePage(): void { + CoreLoginHelper.openInAppForEdit( + CoreSites.getCurrentSiteId(), + '/user/edit.php', + undefined, + true, + ); + this.editingProfile = true; + this.detectProileEdited(); + } + + /** + * Login the user. + */ + login(): void { + CoreNavigator.navigateToSiteHome(); + this.editingProfile = false; + } + + /** + * Logout the user. + */ + logout(): void { + CoreSites.logout(); + this.editingProfile = false; + } + + /** + * Try to detect if the user edited the profile in browser. + */ + detectProileEdited(): void { + if (this.urlLoadedObserver) { + // Already listening (shouldn't happen). + return; + } + + this.urlLoadedObserver = CoreEvents.on(CoreEvents.IAB_LOAD_START, (event) => { + if (event.url.match(/\/user\/preferences.php/)) { + // Profile should be complete now. + CoreUtils.closeInAppBrowser(); + this.login(); + } + }); + + this.browserClosedObserver = CoreEvents.on(CoreEvents.IAB_EXIT, () => { + this.urlLoadedObserver?.off(); + this.browserClosedObserver?.off(); + delete this.urlLoadedObserver; + delete this.browserClosedObserver; + }); + } + + /** + * @inheritdoc + */ + ngOnDestroy(): void { + this.urlLoadedObserver?.off(); + this.browserClosedObserver?.off(); + } + +} diff --git a/src/core/features/user/services/user-helper.ts b/src/core/features/user/services/user-helper.ts index ba08890b2..5e3779eb5 100644 --- a/src/core/features/user/services/user-helper.ts +++ b/src/core/features/user/services/user-helper.ts @@ -13,6 +13,8 @@ // limitations under the License. import { Injectable } from '@angular/core'; +import { CoreNavigator } from '@services/navigator'; +import { CoreSites } from '@services/sites'; import { makeSingleton, Translate } from '@singletons'; import { CoreUserRole } from './user'; @@ -60,6 +62,25 @@ export class CoreUserHelperProvider { }).join(separator + ' '); } + /** + * Open a page with instructions on how to complete profile. + * + * @param siteId The site ID. + */ + async openCompleteProfile(siteId: string): Promise { + const currentSite = CoreSites.getCurrentSite(); + if (!currentSite || siteId !== currentSite.getId()) { + return; // Site that triggered the event is not current site. + } + + // If current page is already complete profile, stop. + if (CoreNavigator.isCurrent('/user/completeprofile')) { + return; + } + + await CoreNavigator.navigate('/user/completeprofile', { params: { siteId }, reset: true }); + } + } export const CoreUserHelper = makeSingleton(CoreUserHelperProvider); diff --git a/src/core/features/user/user-app-lazy.module.ts b/src/core/features/user/user-app-lazy.module.ts new file mode 100644 index 000000000..d036817e8 --- /dev/null +++ b/src/core/features/user/user-app-lazy.module.ts @@ -0,0 +1,37 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// 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. + +import { CoreSharedModule } from '@/core/shared.module'; +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { CoreUserCompleteProfilePage } from './pages/complete-profile/complete-profile'; + +const routes: Routes = [ + { + path: 'completeprofile', + component: CoreUserCompleteProfilePage, + }, +]; + +@NgModule({ + imports: [ + RouterModule.forChild(routes), + CoreSharedModule, + ], + declarations: [ + CoreUserCompleteProfilePage, + ], + exports: [RouterModule], +}) +export class CoreUserAppLazyModule {} diff --git a/src/core/features/user/user.module.ts b/src/core/features/user/user.module.ts index a8e2398a5..c5816dabe 100644 --- a/src/core/features/user/user.module.ts +++ b/src/core/features/user/user.module.ts @@ -35,7 +35,7 @@ import { CoreUserProvider } from './services/user'; import { CoreUserHelperProvider } from './services/user-helper'; import { CoreUserOfflineProvider } from './services/user-offline'; import { CoreUserSyncProvider } from './services/user-sync'; -import { conditionalRoutes } from '@/app/app-routing.module'; +import { AppRoutingModule, conditionalRoutes } from '@/app/app-routing.module'; import { CoreScreen } from '@services/screen'; import { COURSE_PAGE_NAME } from '@features/course/course.module'; import { COURSE_INDEX_PATH } from '@features/course/course-lazy.module'; @@ -51,6 +51,13 @@ export const CORE_USER_SERVICES: Type[] = [ export const PARTICIPANTS_PAGE_NAME = 'participants'; +const appRoutes: Routes = [ + { + path: 'user', + loadChildren: () => import('@features/user/user-app-lazy.module').then(m => m.CoreUserAppLazyModule), + }, +]; + const routes: Routes = [ { path: 'user', @@ -76,6 +83,7 @@ const courseIndexRoutes: Routes = [ @NgModule({ imports: [ + AppRoutingModule.forChild(appRoutes), CoreMainMenuTabRoutingModule.forChild(routes), CoreCourseIndexRoutingModule.forChild({ children: courseIndexRoutes }), CoreUserComponentsModule, diff --git a/src/core/lang.json b/src/core/lang.json index c83d0aed7..e61537e0e 100644 --- a/src/core/lang.json +++ b/src/core/lang.json @@ -340,6 +340,7 @@ "vieweditor": "View editor", "viewembeddedcontent": "View embedded content", "viewprofile": "View profile", + "wanttochangesite": "Want to change sites or log out?", "warningofflinedatadeleted": "Offline data from {{component}} '{{name}}' has been deleted. {{error}}", "warnopeninbrowser": "

You are about to leave the app to open the following URL in your device's browser. Do you want to continue?

\n

{{url}}

", "week": "Week", From 6a12da2bf180d7f0cf4aeecec830a791ad3dc0fd Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 29 Jul 2022 10:04:13 +0200 Subject: [PATCH 3/4] MOBILE-4034 behat: Add tests for complete profile and change pwd --- .../tests/behat/behat_app.php | 6 ++- .../login/tests/behat/basic_usage.feature | 49 +++++++++++++++++ .../user/tests/behat/basic_usage.feature | 53 +++++++++++++++++++ 3 files changed, 106 insertions(+), 2 deletions(-) create mode 100755 src/core/features/user/tests/behat/basic_usage.feature diff --git a/local_moodleappbehat/tests/behat/behat_app.php b/local_moodleappbehat/tests/behat/behat_app.php index 59ece39db..e7f0ed2f0 100644 --- a/local_moodleappbehat/tests/behat/behat_app.php +++ b/local_moodleappbehat/tests/behat/behat_app.php @@ -273,8 +273,10 @@ class behat_app extends behat_app_helper { // Wait until the main page appears. $this->spin( function($context, $args) { - $mainmenu = $context->getSession()->getPage()->find('xpath', '//page-core-mainmenu'); - if ($mainmenu) { + $initialpage = $context->getSession()->getPage()->find('xpath', '//page-core-mainmenu') ?? + $context->getSession()->getPage()->find('xpath', '//page-core-login-change-password') ?? + $context->getSession()->getPage()->find('xpath', '//page-core-user-complete-profile'); + if ($initialpage) { return true; } throw new DriverException('Moodle App main page not loaded after login'); diff --git a/src/core/features/login/tests/behat/basic_usage.feature b/src/core/features/login/tests/behat/basic_usage.feature index 62ffc00fe..d92501232 100755 --- a/src/core/features/login/tests/behat/basic_usage.feature +++ b/src/core/features/login/tests/behat/basic_usage.feature @@ -90,3 +90,52 @@ Feature: Test basic usage of login in app | minimumversion | 11.0.0 | tool_mobile | When I enter the app Then I should find "App update required" in the app + + Scenario: Force password change + Given I force a password change for user "student1" + When I enter the app + And I log in as "student1" + Then I should find "Change your password" in the app + And I should find "You must change your password to proceed." in the app + + When I press "Change password" in the app + Then the app should have opened a browser tab with url "webserver" + + When I close the browser tab opened by the app + Then I should find "If you didn't change your password correctly, you'll be asked to do it again." in the app + But I should not find "Change your password" in the app + + When I press "Reconnect" in the app + Then I should find "Change your password" in the app + But I should not find "Reconnect" in the app + + When I press "Switch account" in the app + Then I should find "Accounts" in the app + And I should find "david student" in the app + + When I press "david student" in the app + Then I should find "Change your password" in the app + But I should not find "Reconnect" in the app + + When I press "Change password" in the app + Then the app should have opened a browser tab with url "webserver" + + When I switch to the browser tab opened by the app + And I set the field "username" to "student1" + And I set the field "password" to "student1" + And I click on "Log in" "button" + Then I should see "You must change your password to proceed" + + When I set the field "Current password" to "student1" + And I set the field "New password" to "NewPassword1*" + And I set the field "New password (again)" to "NewPassword1*" + And I click on "Sign out everywhere" "checkbox" + And I click on "Save changes" "button" + Then I should see "Password has been changed" + + When I close the browser tab opened by the app + Then I should find "If you didn't change your password correctly, you'll be asked to do it again." in the app + But I should not find "Change your password" in the app + + When I press "Reconnect" in the app + Then I should find "Acceptance test site" in the app diff --git a/src/core/features/user/tests/behat/basic_usage.feature b/src/core/features/user/tests/behat/basic_usage.feature new file mode 100755 index 000000000..4aa6b8401 --- /dev/null +++ b/src/core/features/user/tests/behat/basic_usage.feature @@ -0,0 +1,53 @@ +@core @core_user @app @javascript +Feature: Test basic usage of user features + + Background: + Given the following "users" exist: + | username | firstname | lastname | + | student1 | Student | Student | + + Scenario: Complete missing fields + Given the following "custom profile fields" exist: + | datatype | shortname | name | required | + | text | food | Favourite food | 1 | + When I enter the app + And I log in as "student1" + Then I should find "Complete your profile" in the app + And I should find "Before you continue, please fill in the required fields in your user profile." in the app + + When I press "Complete profile" in the app + Then the app should have opened a browser tab with url "webserver" + + When I close the browser tab opened by the app + Then I should find "If you didn't complete your profile correctly, you'll be asked to do it again." in the app + But I should not find "Complete your profile" in the app + + When I press "Reconnect" in the app + Then I should find "Complete your profile" in the app + But I should not find "Reconnect" in the app + + When I press "Switch account" in the app + Then I should find "Accounts" in the app + And I should find "Student Student" in the app + + When I press "Student Student" in the app + Then I should find "Complete your profile" in the app + But I should not find "Reconnect" in the app + + When I press "Complete profile" in the app + Then the app should have opened a browser tab with url "webserver" + + When I switch to the browser tab opened by the app + And I set the field "username" to "student1" + And I set the field "password" to "student1" + And I click on "Log in" "button" + And I set the field "Favourite food" to "Pasta" + And I click on "Update profile" "button" + Then I should see "Changes saved" + + When I close the browser tab opened by the app + Then I should find "If you didn't complete your profile correctly, you'll be asked to do it again." in the app + But I should not find "Complete your profile" in the app + + When I press "Reconnect" in the app + Then I should find "Acceptance test site" in the app From 55d5f9419ba70650efd5c394022e267ecf2eb58f Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Thu, 4 Aug 2022 09:54:09 +0200 Subject: [PATCH 4/4] MOBILE-4034 core: Remove some listeners from app component --- src/app/app.component.ts | 29 ++----------------- src/core/features/login/login.module.ts | 15 +++++++++- .../features/login/services/login-helper.ts | 6 ++-- .../features/user/services/user-helper.ts | 6 ++-- src/core/features/user/user.module.ts | 7 ++++- 5 files changed, 31 insertions(+), 32 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 5b75b9b9a..a4f2c7c0f 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -34,7 +34,6 @@ import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreDom } from '@singletons/dom'; import { CorePlatform } from '@services/platform'; -import { CoreUserHelper } from '@features/user/services/user-helper'; const MOODLE_VERSION_PREFIX = 'version-'; const MOODLEAPP_VERSION_PREFIX = 'moodleapp-'; @@ -53,13 +52,6 @@ export class AppComponent implements OnInit, AfterViewInit { /** * Component being initialized. - * - * @todo Review all old code to see if something is missing: - * - IAB events listening. - * - Platform pause/resume subscriptions. - * - handleOpenURL and openWindowSafely. - * - Back button registering to close modal first. - * - Note: HideKeyboardFormAccessoryBar has been moved to config.xml. */ ngOnInit(): void { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -101,24 +93,6 @@ export class AppComponent implements OnInit, AfterViewInit { content.classList.toggle('core-footer-shadow', !CoreDom.scrollIsBottom(scrollElement)); }); - // Listen for session expired events. - CoreEvents.on(CoreEvents.SESSION_EXPIRED, (data) => { - CoreLoginHelper.sessionExpired(data); - }); - - // Listen for passwordchange and usernotfullysetup events to open InAppBrowser. - CoreEvents.on(CoreEvents.PASSWORD_CHANGE_FORCED, (data) => { - CoreLoginHelper.passwordChangeForced(data.siteId!); - }); - CoreEvents.on(CoreEvents.USER_NOT_FULLY_SETUP, (data) => { - CoreUserHelper.openCompleteProfile(data.siteId!); - }); - - // Listen for sitepolicynotagreed event to accept the site policy. - CoreEvents.on(CoreEvents.SITE_POLICY_NOT_AGREED, (data) => { - CoreLoginHelper.sitePolicyNotAgreed(data.siteId); - }); - // Check URLs loaded in any InAppBrowser. CoreEvents.on(CoreEvents.IAB_LOAD_START, (event) => { // URLs with a custom scheme can be prefixed with "http://" or "https://", we need to remove this. @@ -280,6 +254,9 @@ export class AppComponent implements OnInit, AfterViewInit { CoreApp.closeApp(); }); }); + + // @todo: Pause Youtube videos in Android when app is put in background or screen is locked? + // See: https://github.com/moodlehq/moodleapp/blob/ionic3/src/app/app.component.ts#L312 } /** diff --git a/src/core/features/login/login.module.ts b/src/core/features/login/login.module.ts index fdbb39639..47102a5bf 100644 --- a/src/core/features/login/login.module.ts +++ b/src/core/features/login/login.module.ts @@ -16,10 +16,11 @@ import { APP_INITIALIZER, NgModule } from '@angular/core'; import { Routes } from '@angular/router'; import { AppRoutingModule } from '@/app/app-routing.module'; -import { CoreLoginHelperProvider } from './services/login-helper'; +import { CoreLoginHelper, CoreLoginHelperProvider } from './services/login-helper'; import { CoreRedirectGuard } from '@guards/redirect'; import { CoreLoginCronHandler } from './services/handlers/cron'; import { CoreCronDelegate } from '@services/cron'; +import { CoreEvents } from '@singletons/events'; export const CORE_LOGIN_SERVICES = [ CoreLoginHelperProvider, @@ -44,6 +45,18 @@ const appRoutes: Routes = [ multi: true, useValue: () => { CoreCronDelegate.register(CoreLoginCronHandler.instance); + + CoreEvents.on(CoreEvents.SESSION_EXPIRED, (data) => { + CoreLoginHelper.sessionExpired(data); + }); + + CoreEvents.on(CoreEvents.PASSWORD_CHANGE_FORCED, (data) => { + CoreLoginHelper.passwordChangeForced(data.siteId); + }); + + CoreEvents.on(CoreEvents.SITE_POLICY_NOT_AGREED, (data) => { + CoreLoginHelper.sitePolicyNotAgreed(data.siteId); + }); }, }, ], diff --git a/src/core/features/login/services/login-helper.ts b/src/core/features/login/services/login-helper.ts index f12480039..d56be6fb1 100644 --- a/src/core/features/login/services/login-helper.ts +++ b/src/core/features/login/services/login-helper.ts @@ -784,10 +784,12 @@ export class CoreLoginHelperProvider { /** * Function that should be called when password change is forced. Reserved for core use. * - * @param siteId The site ID. + * @param siteId The site ID. Undefined for current site. */ - async passwordChangeForced(siteId: string): Promise { + async passwordChangeForced(siteId?: string): Promise { const currentSite = CoreSites.getCurrentSite(); + siteId = siteId ?? currentSite?.getId(); + if (!currentSite || siteId !== currentSite.getId()) { return; // Site that triggered the event is not current site. } diff --git a/src/core/features/user/services/user-helper.ts b/src/core/features/user/services/user-helper.ts index 5e3779eb5..3448fac48 100644 --- a/src/core/features/user/services/user-helper.ts +++ b/src/core/features/user/services/user-helper.ts @@ -65,10 +65,12 @@ export class CoreUserHelperProvider { /** * Open a page with instructions on how to complete profile. * - * @param siteId The site ID. + * @param siteId The site ID. Undefined for current site. */ - async openCompleteProfile(siteId: string): Promise { + async openCompleteProfile(siteId?: string): Promise { const currentSite = CoreSites.getCurrentSite(); + siteId = siteId ?? currentSite?.getId(); + if (!currentSite || siteId !== currentSite.getId()) { return; // Site that triggered the event is not current site. } diff --git a/src/core/features/user/user.module.ts b/src/core/features/user/user.module.ts index c5816dabe..d231658db 100644 --- a/src/core/features/user/user.module.ts +++ b/src/core/features/user/user.module.ts @@ -32,13 +32,14 @@ import { CoreCourseOptionsDelegate } from '@features/course/services/course-opti import { CoreUserCourseOptionHandler } from './services/handlers/course-option'; import { CoreUserProfileFieldDelegateService } from './services/user-profile-field-delegate'; import { CoreUserProvider } from './services/user'; -import { CoreUserHelperProvider } from './services/user-helper'; +import { CoreUserHelper, CoreUserHelperProvider } from './services/user-helper'; import { CoreUserOfflineProvider } from './services/user-offline'; import { CoreUserSyncProvider } from './services/user-sync'; import { AppRoutingModule, conditionalRoutes } from '@/app/app-routing.module'; import { CoreScreen } from '@services/screen'; import { COURSE_PAGE_NAME } from '@features/course/course.module'; import { COURSE_INDEX_PATH } from '@features/course/course-lazy.module'; +import { CoreEvents } from '@singletons/events'; export const CORE_USER_SERVICES: Type[] = [ CoreUserDelegateService, @@ -106,6 +107,10 @@ const courseIndexRoutes: Routes = [ CoreCronDelegate.register(CoreUserSyncCronHandler.instance); CoreTagAreaDelegate.registerHandler(CoreUserTagAreaHandler.instance); CoreCourseOptionsDelegate.registerHandler(CoreUserCourseOptionHandler.instance); + + CoreEvents.on(CoreEvents.USER_NOT_FULLY_SETUP, (data) => { + CoreUserHelper.openCompleteProfile(data.siteId); + }); }, }, ],