From 77c6ad411f269fa3be5fda11439b6f1b5dd6d7c2 Mon Sep 17 00:00:00 2001
From: Hiroki Terashima
Date: Mon, 25 Jan 2021 11:50:01 -0800
Subject: [PATCH 1/3] Implemented unlinking Google account from WISE for
teachers and students. #2870
---
.../teacher/TeacherAPIController.java | 8 -
.../user/GoogleUserAPIController.java | 84 ++++
.../controllers/user/UserAPIController.java | 31 +-
.../exception/InvalidPasswordExcpetion.java | 6 +
src/main/resources/i18n/i18n.properties | 6 +
.../copy-project-dialog.component.ts | 1 -
.../edit-password.component.html | 10 +-
.../edit-password.component.spec.ts | 146 ++++---
.../edit-password/edit-password.component.ts | 26 +-
.../src/app/modules/shared/shared.module.ts | 10 +-
...link-google-account-confirm.component.html | 17 +
...link-google-account-confirm.component.scss | 3 +
...k-google-account-confirm.component.spec.ts | 37 ++
...unlink-google-account-confirm.component.ts | 16 +
...ink-google-account-password.component.html | 46 +++
...ink-google-account-password.component.scss | 3 +
...-google-account-password.component.spec.ts | 51 +++
...nlink-google-account-password.component.ts | 38 ++
...link-google-account-success.component.html | 12 +
...link-google-account-success.component.scss | 3 +
...unlink-google-account-success.component.ts | 18 +
.../validators/password-match.validator.ts | 13 +
.../src/app/services/user.service.spec.ts | 24 +-
.../site/src/app/services/user.service.ts | 18 +-
.../edit-profile/edit-profile.component.html | 14 +-
.../edit-profile.component.spec.ts | 2 +
.../edit-profile/edit-profile.component.ts | 30 +-
.../edit-profile/edit-profile.component.html | 14 +-
.../edit-profile.component.spec.ts | 2 +
.../edit-profile/edit-profile.component.ts | 26 +-
src/main/webapp/site/src/messages.xlf | 364 ++++++++++++------
.../user/GoogleUserAPIControllerTest.java | 108 ++++++
.../user/UserAPIControllerTest.java | 62 ---
33 files changed, 938 insertions(+), 311 deletions(-)
create mode 100644 src/main/java/org/wise/portal/presentation/web/controllers/user/GoogleUserAPIController.java
create mode 100644 src/main/java/org/wise/portal/presentation/web/exception/InvalidPasswordExcpetion.java
create mode 100644 src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.html
create mode 100644 src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.scss
create mode 100644 src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.spec.ts
create mode 100644 src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.ts
create mode 100644 src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.html
create mode 100644 src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.scss
create mode 100644 src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.spec.ts
create mode 100644 src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.ts
create mode 100644 src/main/webapp/site/src/app/modules/shared/unlink-google-account-success/unlink-google-account-success.component.html
create mode 100644 src/main/webapp/site/src/app/modules/shared/unlink-google-account-success/unlink-google-account-success.component.scss
create mode 100644 src/main/webapp/site/src/app/modules/shared/unlink-google-account-success/unlink-google-account-success.component.ts
create mode 100644 src/main/webapp/site/src/app/modules/shared/validators/password-match.validator.ts
create mode 100644 src/test/java/org/wise/portal/presentation/web/controllers/user/GoogleUserAPIControllerTest.java
diff --git a/src/main/java/org/wise/portal/presentation/web/controllers/teacher/TeacherAPIController.java b/src/main/java/org/wise/portal/presentation/web/controllers/teacher/TeacherAPIController.java
index 79a5d8a107..72da0d69c3 100644
--- a/src/main/java/org/wise/portal/presentation/web/controllers/teacher/TeacherAPIController.java
+++ b/src/main/java/org/wise/portal/presentation/web/controllers/teacher/TeacherAPIController.java
@@ -14,7 +14,6 @@
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.MessageSource;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.core.Authentication;
@@ -38,7 +37,6 @@
import org.wise.portal.presentation.web.response.SimpleResponse;
import org.wise.portal.service.authentication.DuplicateUsernameException;
import org.wise.portal.service.authentication.UserDetailsService;
-import org.wise.portal.service.mail.IMailFacade;
/**
* Teacher REST API
@@ -55,12 +53,6 @@ public class TeacherAPIController extends UserAPIController {
@Autowired
private UserDetailsService userDetailsService;
- @Autowired
- protected IMailFacade mailService;
-
- @Autowired
- protected MessageSource messageSource;
-
@Value("${google.clientId:}")
private String googleClientId;
diff --git a/src/main/java/org/wise/portal/presentation/web/controllers/user/GoogleUserAPIController.java b/src/main/java/org/wise/portal/presentation/web/controllers/user/GoogleUserAPIController.java
new file mode 100644
index 0000000000..d2cba48c11
--- /dev/null
+++ b/src/main/java/org/wise/portal/presentation/web/controllers/user/GoogleUserAPIController.java
@@ -0,0 +1,84 @@
+package org.wise.portal.presentation.web.controllers.user;
+
+import java.util.HashMap;
+import java.util.Locale;
+
+import javax.mail.MessagingException;
+
+import org.springframework.security.access.annotation.Secured;
+import org.springframework.security.core.Authentication;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.wise.portal.domain.authentication.impl.PersistentUserDetails;
+import org.wise.portal.domain.authentication.impl.TeacherUserDetails;
+import org.wise.portal.domain.user.User;
+import org.wise.portal.presentation.web.exception.InvalidPasswordExcpetion;
+
+@RestController
+@RequestMapping("/api/google-user")
+public class GoogleUserAPIController extends UserAPIController {
+
+ @GetMapping("/check-user-exists")
+ boolean isGoogleIdExist(@RequestParam String googleUserId) {
+ return userService.retrieveUserByGoogleUserId(googleUserId) != null;
+ }
+
+ @GetMapping("/check-user-matches")
+ boolean isGoogleIdMatches(@RequestParam String googleUserId, @RequestParam String userId) {
+ User user = userService.retrieveUserByGoogleUserId(googleUserId);
+ return user != null && user.getId().toString().equals(userId);
+ }
+
+ @GetMapping("/get-user")
+ HashMap getUserByGoogleId(@RequestParam String googleUserId) {
+ User user = userService.retrieveUserByGoogleUserId(googleUserId);
+ HashMap response = new HashMap();
+ if (user == null) {
+ response.put("status", "error");
+ } else {
+ response.put("status", "success");
+ response.put("userId", user.getId());
+ response.put("username", user.getUserDetails().getUsername());
+ response.put("firstName", user.getUserDetails().getFirstname());
+ response.put("lastName", user.getUserDetails().getLastname());
+ }
+ return response;
+ }
+
+ @Secured("ROLE_USER")
+ @PostMapping("/unlink-account")
+ HashMap unlinkGoogleAccount(Authentication auth, @RequestParam String newPassword)
+ throws InvalidPasswordExcpetion {
+ if (newPassword.isEmpty()) {
+ throw new InvalidPasswordExcpetion();
+ }
+ String username = auth.getName();
+ User user = userService.retrieveUserByUsername(username);
+ ((PersistentUserDetails) user.getUserDetails()).setGoogleUserId(null);
+ userService.updateUserPassword(user, newPassword);
+ boolean isSendEmail = Boolean.parseBoolean(appProperties.getProperty("send_email_enabled", "false"));
+ if (isSendEmail && user.isTeacher()) {
+ this.sendUnlinkGoogleEmail((TeacherUserDetails) user.getUserDetails());
+ }
+ return this.getUserInfo(auth, username);
+ }
+
+ private void sendUnlinkGoogleEmail(TeacherUserDetails userDetails) {
+ String[] recipients = { userDetails.getEmailAddress() };
+ String subject = messageSource.getMessage("unlink_google_account_success_email_subject", null,
+ "Successfully Unlinked Google Account", new Locale(userDetails.getLanguage()));
+ String username = userDetails.getUsername();
+ String message = messageSource.getMessage("unlink_google_account_success_email_body",
+ new Object[]{username},
+ "You have unlinked your Google account from WISE. To sign in to WISE in the future, please use your username and the password you just created. Your username is: " + username,
+ new Locale(userDetails.getLanguage()));
+ try {
+ mailService.postMail(recipients, subject, message, appProperties.getProperty("portalemailaddress"));
+ } catch (MessagingException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/org/wise/portal/presentation/web/controllers/user/UserAPIController.java b/src/main/java/org/wise/portal/presentation/web/controllers/user/UserAPIController.java
index b6ef0251f3..21673c81d5 100644
--- a/src/main/java/org/wise/portal/presentation/web/controllers/user/UserAPIController.java
+++ b/src/main/java/org/wise/portal/presentation/web/controllers/user/UserAPIController.java
@@ -12,6 +12,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.MessageSource;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.switchuser.SwitchUserFilter;
@@ -63,6 +64,9 @@ public class UserAPIController {
@Autowired
protected IMailFacade mailService;
+ @Autowired
+ protected MessageSource messageSource;
+
@Value("${google.clientId:}")
protected String googleClientId = "";
@@ -189,33 +193,6 @@ List> getSupportedLanguages() {
return langs;
}
- @GetMapping("/check-google-user-exists")
- boolean isGoogleIdExist(@RequestParam String googleUserId) {
- return userService.retrieveUserByGoogleUserId(googleUserId) != null;
- }
-
- @GetMapping("/check-google-user-matches")
- boolean isGoogleIdMatches(@RequestParam String googleUserId, @RequestParam String userId) {
- User user = userService.retrieveUserByGoogleUserId(googleUserId);
- return user != null && user.getId().toString().equals(userId);
- }
-
- @GetMapping("/google-user")
- HashMap getUserByGoogleId(@RequestParam String googleUserId) {
- User user = userService.retrieveUserByGoogleUserId(googleUserId);
- HashMap response = new HashMap();
- if (user == null) {
- response.put("status", "error");
- } else {
- response.put("status", "success");
- response.put("userId", user.getId());
- response.put("username", user.getUserDetails().getUsername());
- response.put("firstName", user.getUserDetails().getFirstname());
- response.put("lastName", user.getUserDetails().getLastname());
- }
- return response;
- }
-
private String getLanguageName(String localeString) {
if (localeString.toLowerCase().equals("zh_tw")) {
return "Chinese (Traditional)";
diff --git a/src/main/java/org/wise/portal/presentation/web/exception/InvalidPasswordExcpetion.java b/src/main/java/org/wise/portal/presentation/web/exception/InvalidPasswordExcpetion.java
new file mode 100644
index 0000000000..6d6dac6cbc
--- /dev/null
+++ b/src/main/java/org/wise/portal/presentation/web/exception/InvalidPasswordExcpetion.java
@@ -0,0 +1,6 @@
+package org.wise.portal.presentation.web.exception;
+
+public class InvalidPasswordExcpetion extends Exception {
+
+ private static final long serialVersionUID = 1L;
+}
diff --git a/src/main/resources/i18n/i18n.properties b/src/main/resources/i18n/i18n.properties
index 7c050e8dcb..1ffd28e71c 100644
--- a/src/main/resources/i18n/i18n.properties
+++ b/src/main/resources/i18n/i18n.properties
@@ -308,6 +308,12 @@ teacher_cap.description=Text for the word "Teacher"
team_cap=Team
team_cap.description=Text for the word "Team"
+unlink_google_account_success_email_subject=Successfully Unlinked Google Account
+unlink_google_account_success_email_subject.description=Subject text in email to notify user about successfuly unlinking google account
+
+unlink_google_account_success_email_body=You have unlinked your Google account from WISE. To sign in to WISE in the future, please use your username and the password you just created.\n\nYour username is: {0}\n\nThank you for using WISE,\nWISE Team
+unlink_google_account_success_email_body.description=Body text in email to notify user about successfully unlinking google account
+
# Root (/) Pages #
accountmenu.forgot=Forgot Username or Password?
diff --git a/src/main/webapp/site/src/app/modules/library/copy-project-dialog/copy-project-dialog.component.ts b/src/main/webapp/site/src/app/modules/library/copy-project-dialog/copy-project-dialog.component.ts
index 8a2db347d5..5347591222 100644
--- a/src/main/webapp/site/src/app/modules/library/copy-project-dialog/copy-project-dialog.component.ts
+++ b/src/main/webapp/site/src/app/modules/library/copy-project-dialog/copy-project-dialog.component.ts
@@ -5,7 +5,6 @@ import { finalize } from 'rxjs/operators';
import { LibraryProject } from '../libraryProject';
import { LibraryService } from '../../../services/library.service';
import { MatSnackBar } from '@angular/material/snack-bar';
-import { Subscription } from 'rxjs';
@Component({
selector: 'app-copy-project-dialog',
diff --git a/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.html b/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.html
index 1a47a25674..71af1f2b7d 100644
--- a/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.html
+++ b/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.html
@@ -57,5 +57,13 @@
- This account was created using Google and doesn't use a WISE password. If you would like to unlink your Google account, please contact us .
+ This account was created using Google and doesn't use a WISE password.
+
+
+ link_off
+ Unlink Google Account
+
+
diff --git a/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.spec.ts b/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.spec.ts
index 6d33a734a5..fd68f182c1 100644
--- a/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.spec.ts
+++ b/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.spec.ts
@@ -1,14 +1,17 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { EditPasswordComponent } from './edit-password.component';
import { UserService } from '../../../services/user.service';
-import { BehaviorSubject, Observable } from 'rxjs';
+import { BehaviorSubject, Observable, of } from 'rxjs';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ReactiveFormsModule } from '@angular/forms';
-import { NO_ERRORS_SCHEMA, Provider } from '@angular/core';
+import { NO_ERRORS_SCHEMA } from '@angular/core';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { By } from '@angular/platform-browser';
import { User } from '../../../domain/user';
import { configureTestSuite } from 'ng-bullet';
+import { MatDialogModule } from '@angular/material/dialog';
+const CORRECT_OLD_PASS = 'a';
+const INCORRECT_OLD_PASS = 'b';
export class MockUserService {
getUser(): BehaviorSubject {
@@ -22,13 +25,13 @@ export class MockUserService {
}
changePassword(username, oldPassword, newPassword) {
- if (oldPassword === 'a') {
- return Observable.create((observer) => {
+ if (oldPassword === CORRECT_OLD_PASS) {
+ return new Observable((observer) => {
observer.next({ status: 'success', messageCode: 'passwordChanged' });
observer.complete();
});
} else {
- return Observable.create((observer) => {
+ return new Observable((observer) => {
observer.next({ status: 'error', messageCode: 'incorrectPassword' });
observer.complete();
});
@@ -36,22 +39,26 @@ export class MockUserService {
}
}
-describe('EditPasswordComponent', () => {
- let component: EditPasswordComponent;
- let fixture: ComponentFixture;
+let component: EditPasswordComponent;
+let fixture: ComponentFixture;
+
+const getSubmitButton = () => {
+ return fixture.debugElement.nativeElement.querySelector('button[type="submit"]');
+};
- const getSubmitButton = () => {
- return fixture.debugElement.nativeElement.querySelector('button[type="submit"]');
- };
+const getUnlinkGoogleAccountButton = () => {
+ return fixture.debugElement.nativeElement.querySelector('button[id="unlinkGoogleAccount"]');
+};
- const getForm = () => {
- return fixture.debugElement.query(By.css('form'));
- };
+const getForm = () => {
+ return fixture.debugElement.query(By.css('form'));
+};
+describe('EditPasswordComponent', () => {
configureTestSuite(() => {
TestBed.configureTestingModule({
declarations: [EditPasswordComponent],
- imports: [BrowserAnimationsModule, ReactiveFormsModule, MatSnackBarModule],
+ imports: [BrowserAnimationsModule, ReactiveFormsModule, MatSnackBarModule, MatDialogModule],
providers: [{ provide: UserService, useValue: new MockUserService() }],
schemas: [NO_ERRORS_SCHEMA]
});
@@ -63,61 +70,60 @@ describe('EditPasswordComponent', () => {
fixture.detectChanges();
});
- it('should create', () => {
- expect(component).toBeTruthy();
- });
+ initialState_disableSubmitButton();
+ validForm_enableSubmitButton();
+ passwordMismatch_disableSubmitButtonAndInvalidateForm();
+ oldPasswordIncorrect_disableSubmitButtonAndShowError();
+ formSubmit_disableSubmitButton();
+ passwordChanged_handleResponse();
+ incorrectPassword_showError();
+ notGoogleUser_showUnlinkOption();
+ unlinkGoogleButtonClick_showDialog();
+});
+function initialState_disableSubmitButton() {
it('should disable submit button and invalidate form on initial state', () => {
expect(component.changePasswordFormGroup.valid).toBeFalsy();
- const submitButton = getSubmitButton();
- expect(submitButton.disabled).toBe(true);
+ expectSubmitButtonDisabled();
});
+}
+function validForm_enableSubmitButton() {
it('should enable submit button when form is valid', () => {
- component.changePasswordFormGroup.get('oldPassword').setValue('a');
- component.newPasswordFormGroup.get('newPassword').setValue('b');
- component.newPasswordFormGroup.get('confirmNewPassword').setValue('b');
- fixture.detectChanges();
- const submitButton = getSubmitButton();
- expect(submitButton.disabled).toBe(false);
+ setPasswords(CORRECT_OLD_PASS, 'b', 'b');
+ expectSubmitButtonEnabled();
expect(component.changePasswordFormGroup.valid).toBeTruthy();
});
+}
+function passwordMismatch_disableSubmitButtonAndInvalidateForm() {
it('should disable submit button and invalidate form when new password and confirm new password fields do not match', () => {
- component.changePasswordFormGroup.get('oldPassword').setValue('a');
- component.newPasswordFormGroup.get('newPassword').setValue('a');
- component.newPasswordFormGroup.get('confirmNewPassword').setValue('b');
- fixture.detectChanges();
- const submitButton = getSubmitButton();
- expect(submitButton.disabled).toBe(true);
+ setPasswords(CORRECT_OLD_PASS, 'a', 'b');
+ expectSubmitButtonDisabled();
expect(component.changePasswordFormGroup.valid).toBeFalsy();
});
+}
+function oldPasswordIncorrect_disableSubmitButtonAndShowError() {
it('should disable submit button and set incorrectPassword error when old password is incorrect', async () => {
- component.changePasswordFormGroup.get('oldPassword').setValue('b');
- component.newPasswordFormGroup.get('newPassword').setValue('c');
- component.newPasswordFormGroup.get('confirmNewPassword').setValue('c');
- const form = getForm();
- form.triggerEventHandler('submit', null);
- fixture.detectChanges();
- const submitButton = getSubmitButton();
- expect(submitButton.disabled).toBe(true);
+ setPasswords(INCORRECT_OLD_PASS, 'c', 'c');
+ submitForm();
+ expectSubmitButtonDisabled();
expect(component.changePasswordFormGroup.get('oldPassword').getError('incorrectPassword')).toBe(
true
);
});
+}
+function formSubmit_disableSubmitButton() {
it('should disable submit button when form is successfully submitted', async () => {
- component.changePasswordFormGroup.get('oldPassword').setValue('a');
- component.newPasswordFormGroup.get('newPassword').setValue('b');
- component.newPasswordFormGroup.get('confirmNewPassword').setValue('b');
- const form = getForm();
- form.triggerEventHandler('submit', null);
- fixture.detectChanges();
- const submitButton = getSubmitButton();
- expect(submitButton.disabled).toBe(true);
+ setPasswords(CORRECT_OLD_PASS, 'b', 'b');
+ submitForm();
+ expectSubmitButtonDisabled();
});
+}
+function passwordChanged_handleResponse() {
it('should handle the change password response when the password was successfully changed', () => {
const resetFormSpy = spyOn(component, 'resetForm');
const snackBarSpy = spyOn(component.snackBar, 'open');
@@ -129,7 +135,9 @@ describe('EditPasswordComponent', () => {
expect(resetFormSpy).toHaveBeenCalled();
expect(snackBarSpy).toHaveBeenCalled();
});
+}
+function incorrectPassword_showError() {
it('should handle the change password response when the password was incorrect', () => {
const response = {
status: 'error',
@@ -140,4 +148,44 @@ describe('EditPasswordComponent', () => {
true
);
});
-});
+}
+
+function notGoogleUser_showUnlinkOption() {
+ it('should hide show option to unlink google account if the user is not a google user', () => {
+ expect(getUnlinkGoogleAccountButton()).toBeNull();
+ });
+}
+
+function unlinkGoogleButtonClick_showDialog() {
+ it('clicking on unlink google account link should open a dialog', () => {
+ const dialogSpy = spyOn(component.dialog, 'open');
+ setGoogleUser();
+ getUnlinkGoogleAccountButton().click();
+ expect(dialogSpy).toHaveBeenCalled();
+ });
+}
+
+export function expectSubmitButtonDisabled() {
+ expect(getSubmitButton().disabled).toBe(true);
+}
+
+function expectSubmitButtonEnabled() {
+ expect(getSubmitButton().disabled).toBe(false);
+}
+
+function submitForm() {
+ getForm().triggerEventHandler('submit', null);
+ fixture.detectChanges();
+}
+
+function setGoogleUser() {
+ component.isGoogleUser = true;
+ fixture.detectChanges();
+}
+
+function setPasswords(oldPass: string, newPass: string, newPassConfirm: string) {
+ component.changePasswordFormGroup.get('oldPassword').setValue(oldPass);
+ component.newPasswordFormGroup.get('newPassword').setValue(newPass);
+ component.newPasswordFormGroup.get('confirmNewPassword').setValue(newPassConfirm);
+ fixture.detectChanges();
+}
diff --git a/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.ts b/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.ts
index f22788cc5f..8df43f729b 100644
--- a/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.ts
+++ b/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.ts
@@ -1,15 +1,18 @@
-import { Component, OnInit, ViewChild } from '@angular/core';
+import { Component, ViewChild } from '@angular/core';
import { FormControl, FormGroup, Validators, FormBuilder } from '@angular/forms';
import { finalize } from 'rxjs/operators';
+import { MatDialog } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { UserService } from '../../../services/user.service';
+import { UnlinkGoogleAccountConfirmComponent } from '../unlink-google-account-confirm/unlink-google-account-confirm.component';
+import { passwordMatchValidator } from '../validators/password-match.validator';
@Component({
selector: 'app-edit-password',
templateUrl: './edit-password.component.html',
styleUrls: ['./edit-password.component.scss']
})
-export class EditPasswordComponent implements OnInit {
+export class EditPasswordComponent {
@ViewChild('changePasswordForm', { static: false }) changePasswordForm;
isSaving: boolean = false;
isGoogleUser: boolean = false;
@@ -19,7 +22,7 @@ export class EditPasswordComponent implements OnInit {
newPassword: new FormControl('', [Validators.required]),
confirmNewPassword: new FormControl('', [Validators.required])
},
- { validator: this.passwordMatchValidator }
+ { validator: passwordMatchValidator }
);
changePasswordFormGroup: FormGroup = this.fb.group({
@@ -30,6 +33,7 @@ export class EditPasswordComponent implements OnInit {
constructor(
private fb: FormBuilder,
private userService: UserService,
+ public dialog: MatDialog,
public snackBar: MatSnackBar
) {}
@@ -39,18 +43,6 @@ export class EditPasswordComponent implements OnInit {
});
}
- passwordMatchValidator(passwordsFormGroup: FormGroup) {
- const newPassword = passwordsFormGroup.get('newPassword').value;
- const confirmNewPassword = passwordsFormGroup.get('confirmNewPassword').value;
- if (newPassword === confirmNewPassword) {
- return null;
- } else {
- const error = { passwordDoesNotMatch: true };
- passwordsFormGroup.controls['confirmNewPassword'].setErrors(error);
- return error;
- }
- }
-
saveChanges() {
this.isSaving = true;
const oldPassword: string = this.getControlFieldValue('oldPassword');
@@ -90,6 +82,10 @@ export class EditPasswordComponent implements OnInit {
}
}
+ unlinkGoogleAccount() {
+ this.dialog.open(UnlinkGoogleAccountConfirmComponent);
+ }
+
resetForm() {
this.changePasswordForm.resetForm();
}
diff --git a/src/main/webapp/site/src/app/modules/shared/shared.module.ts b/src/main/webapp/site/src/app/modules/shared/shared.module.ts
index 5ec6a150df..807f04e4e7 100644
--- a/src/main/webapp/site/src/app/modules/shared/shared.module.ts
+++ b/src/main/webapp/site/src/app/modules/shared/shared.module.ts
@@ -5,6 +5,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
+import { MatDialogModule } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
@@ -13,6 +14,7 @@ import { MatSelectModule } from '@angular/material/select';
const materialModules = [
MatButtonModule,
MatCardModule,
+ MatDialogModule,
MatIconModule,
MatInputModule,
MatFormFieldModule,
@@ -26,6 +28,9 @@ import { HeroSectionComponent } from './hero-section/hero-section.component';
import { SearchBarComponent } from './search-bar/search-bar.component';
import { SelectMenuComponent } from './select-menu/select-menu.component';
import { EditPasswordComponent } from './edit-password/edit-password.component';
+import { UnlinkGoogleAccountConfirmComponent } from './unlink-google-account-confirm/unlink-google-account-confirm.component';
+import { UnlinkGoogleAccountPasswordComponent } from './unlink-google-account-password/unlink-google-account-password.component';
+import { UnlinkGoogleAccountSuccessComponent } from './unlink-google-account-success/unlink-google-account-success.component';
@NgModule({
imports: [
@@ -52,7 +57,10 @@ import { EditPasswordComponent } from './edit-password/edit-password.component';
HeroSectionComponent,
SearchBarComponent,
SelectMenuComponent,
- EditPasswordComponent
+ EditPasswordComponent,
+ UnlinkGoogleAccountConfirmComponent,
+ UnlinkGoogleAccountPasswordComponent,
+ UnlinkGoogleAccountSuccessComponent
]
})
export class SharedModule {}
diff --git a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.html b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.html
new file mode 100644
index 0000000000..1787a19ea9
--- /dev/null
+++ b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.html
@@ -0,0 +1,17 @@
+Unlink Google Account
+
+
+
+ If you remove the link to your Google account, you will be asked to create a WISE password. You will then need to sign in to WISE using your username and password in the future.
+
+
Note: You will no longer be able to sign in to this WISE account using Google. Do you want to continue?
+
+
+
+ Cancel
+
+ Continue
+
+
diff --git a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.scss b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.scss
new file mode 100644
index 0000000000..0af6d8c18e
--- /dev/null
+++ b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.scss
@@ -0,0 +1,3 @@
+.info-note {
+ font-weight: bold;
+}
diff --git a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.spec.ts b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.spec.ts
new file mode 100644
index 0000000000..400a72a901
--- /dev/null
+++ b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.spec.ts
@@ -0,0 +1,37 @@
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { MatDialogModule } from '@angular/material/dialog';
+import { configureTestSuite } from 'ng-bullet';
+import { UnlinkGoogleAccountConfirmComponent } from './unlink-google-account-confirm.component';
+
+let component: UnlinkGoogleAccountConfirmComponent;
+let fixture: ComponentFixture;
+
+describe('UnlinkGoogleAccountConfirmComponent', () => {
+ configureTestSuite(() => {
+ TestBed.configureTestingModule({
+ declarations: [UnlinkGoogleAccountConfirmComponent],
+ imports: [MatDialogModule],
+ providers: [],
+ schemas: [NO_ERRORS_SCHEMA]
+ });
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(UnlinkGoogleAccountConfirmComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ continue_closeAllDialogsAndOpenChangePasswordDialog();
+});
+
+function continue_closeAllDialogsAndOpenChangePasswordDialog() {
+ it('continue() should closeAllDialogs and open a new dialog to edit password', () => {
+ const closeAllDialogSpy = spyOn(component.dialog, 'closeAll');
+ const openDialogSpy = spyOn(component.dialog, 'open');
+ component.continue();
+ expect(closeAllDialogSpy).toHaveBeenCalled();
+ expect(openDialogSpy).toHaveBeenCalled();
+ });
+}
diff --git a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.ts b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.ts
new file mode 100644
index 0000000000..73a3e6d7f5
--- /dev/null
+++ b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.ts
@@ -0,0 +1,16 @@
+import { Component } from '@angular/core';
+import { MatDialog } from '@angular/material/dialog';
+import { UnlinkGoogleAccountPasswordComponent } from '../unlink-google-account-password/unlink-google-account-password.component';
+
+@Component({
+ styleUrls: ['./unlink-google-account-confirm.component.scss'],
+ templateUrl: './unlink-google-account-confirm.component.html'
+})
+export class UnlinkGoogleAccountConfirmComponent {
+ constructor(public dialog: MatDialog) {}
+
+ continue() {
+ this.dialog.closeAll();
+ this.dialog.open(UnlinkGoogleAccountPasswordComponent);
+ }
+}
diff --git a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.html b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.html
new file mode 100644
index 0000000000..88f3989891
--- /dev/null
+++ b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.html
@@ -0,0 +1,46 @@
+Unlink Google Account
+
+
+
Create a WISE password:
+
+
+
+
+ Cancel
+
+ Submit
+
+
+
diff --git a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.scss b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.scss
new file mode 100644
index 0000000000..efd343d97f
--- /dev/null
+++ b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.scss
@@ -0,0 +1,3 @@
+mat-dialog-content {
+ min-width: 600px;
+}
diff --git a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.spec.ts b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.spec.ts
new file mode 100644
index 0000000000..d10a68730e
--- /dev/null
+++ b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.spec.ts
@@ -0,0 +1,51 @@
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+import { MatDialogModule } from '@angular/material/dialog';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { configureTestSuite } from 'ng-bullet';
+import { Subscription } from 'rxjs';
+import { UserService } from '../../../services/user.service';
+import { UnlinkGoogleAccountPasswordComponent } from './unlink-google-account-password.component';
+
+class MockUserService {
+ unlinkGoogleUser(newPassword: string) {
+ return new Subscription();
+ }
+}
+
+let component: UnlinkGoogleAccountPasswordComponent;
+let fixture: ComponentFixture;
+let userService = new MockUserService();
+
+describe('UnlinkGoogleAccountPasswordComponent', () => {
+ configureTestSuite(() => {
+ TestBed.configureTestingModule({
+ declarations: [UnlinkGoogleAccountPasswordComponent],
+ imports: [BrowserAnimationsModule, ReactiveFormsModule, MatDialogModule],
+ providers: [{ provide: UserService, useValue: userService }],
+ schemas: [NO_ERRORS_SCHEMA]
+ });
+ });
+ beforeEach(() => {
+ fixture = TestBed.createComponent(UnlinkGoogleAccountPasswordComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+ formSubmit_callUserServiceUnlinkGoogleUserFunction();
+});
+
+function formSubmit_callUserServiceUnlinkGoogleUserFunction() {
+ it('should call UserService.UnlinkGoogleUserFunction when form is submitted', () => {
+ const unlinkFunctionSpy = spyOn(userService, 'unlinkGoogleUser').and.returnValue(
+ new Subscription()
+ );
+ const newPassword = 'aloha';
+ component.newPasswordFormGroup.setValue({
+ newPassword: newPassword,
+ confirmNewPassword: newPassword
+ });
+ component.submit();
+ expect(unlinkFunctionSpy).toHaveBeenCalledWith(newPassword);
+ });
+}
diff --git a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.ts b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.ts
new file mode 100644
index 0000000000..152b168f88
--- /dev/null
+++ b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.ts
@@ -0,0 +1,38 @@
+import { Component } from '@angular/core';
+import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
+import { MatDialog } from '@angular/material/dialog';
+import { UserService } from '../../../services/user.service';
+import { UnlinkGoogleAccountSuccessComponent } from '../unlink-google-account-success/unlink-google-account-success.component';
+import { passwordMatchValidator } from '../validators/password-match.validator';
+
+@Component({
+ styleUrls: ['./unlink-google-account-password.component.scss'],
+ templateUrl: './unlink-google-account-password.component.html'
+})
+export class UnlinkGoogleAccountPasswordComponent {
+ isSaving: boolean = false;
+ newPasswordFormGroup: FormGroup = this.fb.group(
+ {
+ newPassword: new FormControl('', [Validators.required]),
+ confirmNewPassword: new FormControl('', [Validators.required])
+ },
+ { validator: passwordMatchValidator }
+ );
+
+ constructor(
+ private fb: FormBuilder,
+ public dialog: MatDialog,
+ private userService: UserService
+ ) {}
+
+ submit() {
+ this.isSaving = true;
+ this.userService
+ .unlinkGoogleUser(this.newPasswordFormGroup.get('newPassword').value)
+ .add(() => {
+ this.isSaving = false;
+ this.dialog.closeAll();
+ this.dialog.open(UnlinkGoogleAccountSuccessComponent);
+ });
+ }
+}
diff --git a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-success/unlink-google-account-success.component.html b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-success/unlink-google-account-success.component.html
new file mode 100644
index 0000000000..e985c5bb42
--- /dev/null
+++ b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-success/unlink-google-account-success.component.html
@@ -0,0 +1,12 @@
+Unlink Google Account
+
+
+
+ Success! You have unlinked your Google account from WISE. To sign in to WISE in the future, please use your username and password you just created.
+
+
Your username is: {{username}}
+
+
+
+ Done
+
diff --git a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-success/unlink-google-account-success.component.scss b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-success/unlink-google-account-success.component.scss
new file mode 100644
index 0000000000..0af6d8c18e
--- /dev/null
+++ b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-success/unlink-google-account-success.component.scss
@@ -0,0 +1,3 @@
+.info-note {
+ font-weight: bold;
+}
diff --git a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-success/unlink-google-account-success.component.ts b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-success/unlink-google-account-success.component.ts
new file mode 100644
index 0000000000..1701865273
--- /dev/null
+++ b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-success/unlink-google-account-success.component.ts
@@ -0,0 +1,18 @@
+import { Component } from '@angular/core';
+import { Teacher } from '../../../domain/teacher';
+import { UserService } from '../../../services/user.service';
+
+@Component({
+ styleUrls: ['unlink-google-account-success.component.scss'],
+ templateUrl: 'unlink-google-account-success.component.html'
+})
+export class UnlinkGoogleAccountSuccessComponent {
+ username: string;
+
+ constructor(private userService: UserService) {}
+
+ ngOnInit() {
+ const user = this.userService.getUser().getValue();
+ this.username = user.username;
+ }
+}
diff --git a/src/main/webapp/site/src/app/modules/shared/validators/password-match.validator.ts b/src/main/webapp/site/src/app/modules/shared/validators/password-match.validator.ts
new file mode 100644
index 0000000000..89ac014720
--- /dev/null
+++ b/src/main/webapp/site/src/app/modules/shared/validators/password-match.validator.ts
@@ -0,0 +1,13 @@
+import { FormGroup } from '@angular/forms';
+
+export function passwordMatchValidator(passwordsFormGroup: FormGroup) {
+ const newPassword = passwordsFormGroup.get('newPassword').value;
+ const confirmNewPassword = passwordsFormGroup.get('confirmNewPassword').value;
+ if (newPassword === confirmNewPassword) {
+ return null;
+ } else {
+ const error = { passwordDoesNotMatch: true };
+ passwordsFormGroup.controls['confirmNewPassword'].setErrors(error);
+ return error;
+ }
+}
diff --git a/src/main/webapp/site/src/app/services/user.service.spec.ts b/src/main/webapp/site/src/app/services/user.service.spec.ts
index 5cb27e9c91..8671e76ce5 100644
--- a/src/main/webapp/site/src/app/services/user.service.spec.ts
+++ b/src/main/webapp/site/src/app/services/user.service.spec.ts
@@ -1,8 +1,10 @@
-import { TestBed, inject } from '@angular/core/testing';
+import { fakeAsync, TestBed, tick } from '@angular/core/testing';
import { UserService } from './user.service';
-import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { ConfigService } from './config.service';
+let service: UserService;
+let http: HttpTestingController;
export class MockConfigService {}
describe('UserService', () => {
@@ -11,9 +13,21 @@ describe('UserService', () => {
providers: [UserService, { provide: ConfigService, useClass: MockConfigService }],
imports: [HttpClientTestingModule]
});
+ service = TestBed.inject(UserService);
+ http = TestBed.inject(HttpTestingController);
});
+ unlinkGoogleAccount_postToUrl();
+});
- it('should be created', inject([UserService, ConfigService], (service: UserService) => {
- expect(service).toBeTruthy();
+function unlinkGoogleAccount_postToUrl() {
+ it('unlinkGoogleAccount() should make POST request to unlink google account', fakeAsync(() => {
+ const newPassword = 'my new pass';
+ service.unlinkGoogleUser(newPassword);
+ const unlinkRequest = http.expectOne({
+ url: '/api/google-user/unlink-account',
+ method: 'POST'
+ });
+ unlinkRequest.flush({ response: 'success' });
+ tick();
}));
-});
+}
diff --git a/src/main/webapp/site/src/app/services/user.service.ts b/src/main/webapp/site/src/app/services/user.service.ts
index 65d7695e52..b419803a08 100644
--- a/src/main/webapp/site/src/app/services/user.service.ts
+++ b/src/main/webapp/site/src/app/services/user.service.ts
@@ -12,13 +12,14 @@ import { Student } from '../domain/student';
export class UserService {
private userUrl = '/api/user/info';
private user$: BehaviorSubject = new BehaviorSubject(null);
- private checkGoogleUserExistsUrl = '/api/user/check-google-user-exists';
- private checkGoogleUserMatchesUrl = '/api/user/check-google-user-matches';
- private googleUserUrl = '/api/user/google-user';
+ private checkGoogleUserExistsUrl = '/api/google-user/check-user-exists';
+ private checkGoogleUserMatchesUrl = '/api/google-user/check-user-matches';
+ private googleUserUrl = '/api/google-user/get-user';
private checkAuthenticationUrl = '/api/user/check-authentication';
private changePasswordUrl = '/api/user/password';
private languagesUrl = '/api/user/languages';
private contactUrl = '/api/contact';
+ private unlinkGoogleAccountUrl = '/api/google-user/unlink-account';
isAuthenticated = false;
isRecaptchaRequired = false;
redirectUrl: string; // redirect here after logging in
@@ -126,6 +127,17 @@ export class UserService {
return this.http.get(this.checkGoogleUserMatchesUrl, { params: params });
}
+ unlinkGoogleUser(newPassword: string) {
+ const headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded');
+ let body = new HttpParams();
+ body = body.set('newPassword', newPassword);
+ return this.http
+ .post(this.unlinkGoogleAccountUrl, body, { headers: headers })
+ .subscribe((user) => {
+ this.user$.next(user);
+ });
+ }
+
getUserByGoogleId(googleUserId: string) {
let params = new HttpParams();
params = params.set('googleUserId', googleUserId);
diff --git a/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.html b/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.html
index e69a78b318..14fe2c4609 100644
--- a/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.html
+++ b/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.html
@@ -42,7 +42,9 @@
Language required
-
+
Save Changes
+
+ This profile is linked to a Google account.
+
+ link_off
+ Unlink Google Account
+
+
diff --git a/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.spec.ts b/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.spec.ts
index c338a4a343..5c425f8766 100644
--- a/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.spec.ts
+++ b/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.spec.ts
@@ -13,6 +13,7 @@ import { NO_ERRORS_SCHEMA } from '@angular/core';
import { By } from '@angular/platform-browser';
import { Student } from '../../../domain/student';
import { configureTestSuite } from 'ng-bullet';
+import { MatDialogModule } from '@angular/material/dialog';
export class MockUserService {
user: User;
@@ -76,6 +77,7 @@ describe('EditProfileComponent', () => {
imports: [
BrowserAnimationsModule,
ReactiveFormsModule,
+ MatDialogModule,
MatInputModule,
MatSelectModule,
MatSnackBarModule
diff --git a/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.ts b/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.ts
index ea256cd802..afe3d42111 100644
--- a/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.ts
+++ b/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.ts
@@ -5,18 +5,22 @@ import { MatSnackBar } from '@angular/material/snack-bar';
import { Student } from '../../../domain/student';
import { UserService } from '../../../services/user.service';
import { StudentService } from '../../student.service';
+import { Subscription } from 'rxjs';
+import { MatDialog } from '@angular/material/dialog';
+import { UnlinkGoogleAccountConfirmComponent } from '../../../modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component';
@Component({
selector: 'app-edit-profile',
templateUrl: './edit-profile.component.html',
styleUrls: ['./edit-profile.component.scss']
})
-export class EditProfileComponent implements OnInit {
+export class EditProfileComponent {
user: Student;
languages: object[];
changed: boolean = false;
isSaving: boolean = false;
-
+ isGoogleUser: boolean = false;
+ userSubscription: Subscription;
editProfileFormGroup: FormGroup = this.fb.group({
firstName: new FormControl({ value: '', disabled: true }, [Validators.required]),
lastName: new FormControl({ value: '', disabled: true }, [Validators.required]),
@@ -28,6 +32,7 @@ export class EditProfileComponent implements OnInit {
private fb: FormBuilder,
private studentService: StudentService,
private userService: UserService,
+ public dialog: MatDialog,
public snackBar: MatSnackBar
) {
this.user = this.getUser().getValue();
@@ -38,10 +43,6 @@ export class EditProfileComponent implements OnInit {
this.userService.getLanguages().subscribe((response) => {
this.languages = response;
});
-
- this.editProfileFormGroup.valueChanges.subscribe(() => {
- this.changed = true;
- });
}
getUser() {
@@ -52,7 +53,18 @@ export class EditProfileComponent implements OnInit {
this.editProfileFormGroup.controls[name].setValue(value);
}
- ngOnInit() {}
+ ngOnInit() {
+ this.editProfileFormGroup.valueChanges.subscribe(() => {
+ this.changed = true;
+ });
+ this.userSubscription = this.userService.getUser().subscribe((user) => {
+ this.isGoogleUser = user.isGoogleUser;
+ });
+ }
+
+ ngOnDestroy() {
+ this.userSubscription.unsubscribe();
+ }
saveChanges() {
this.isSaving = true;
@@ -84,4 +96,8 @@ export class EditProfileComponent implements OnInit {
}
this.isSaving = false;
}
+
+ unlinkGoogleAccount() {
+ this.dialog.open(UnlinkGoogleAccountConfirmComponent);
+ }
}
diff --git a/src/main/webapp/site/src/app/teacher/account/edit-profile/edit-profile.component.html b/src/main/webapp/site/src/app/teacher/account/edit-profile/edit-profile.component.html
index b4f5bd0f54..a551190844 100644
--- a/src/main/webapp/site/src/app/teacher/account/edit-profile/edit-profile.component.html
+++ b/src/main/webapp/site/src/app/teacher/account/edit-profile/edit-profile.component.html
@@ -109,7 +109,9 @@
-
+
Save Changes
+
+ This profile is linked to a Google account.
+
+ link_off
+ Unlink Google Account
+
+
diff --git a/src/main/webapp/site/src/app/teacher/account/edit-profile/edit-profile.component.spec.ts b/src/main/webapp/site/src/app/teacher/account/edit-profile/edit-profile.component.spec.ts
index e8fe132d86..05b8ad23a2 100644
--- a/src/main/webapp/site/src/app/teacher/account/edit-profile/edit-profile.component.spec.ts
+++ b/src/main/webapp/site/src/app/teacher/account/edit-profile/edit-profile.component.spec.ts
@@ -13,6 +13,7 @@ import { NO_ERRORS_SCHEMA } from '@angular/core';
import { By } from '@angular/platform-browser';
import { User } from '../../../domain/user';
import { configureTestSuite } from 'ng-bullet';
+import { MatDialogModule } from '@angular/material/dialog';
export class MockUserService {
user: User;
@@ -89,6 +90,7 @@ describe('EditProfileComponent', () => {
imports: [
BrowserAnimationsModule,
ReactiveFormsModule,
+ MatDialogModule,
MatInputModule,
MatSelectModule,
MatSnackBarModule
diff --git a/src/main/webapp/site/src/app/teacher/account/edit-profile/edit-profile.component.ts b/src/main/webapp/site/src/app/teacher/account/edit-profile/edit-profile.component.ts
index 6ed9c50c25..a1c09fd71f 100644
--- a/src/main/webapp/site/src/app/teacher/account/edit-profile/edit-profile.component.ts
+++ b/src/main/webapp/site/src/app/teacher/account/edit-profile/edit-profile.component.ts
@@ -5,13 +5,16 @@ import { MatSnackBar } from '@angular/material/snack-bar';
import { UserService } from '../../../services/user.service';
import { Teacher } from '../../../domain/teacher';
import { TeacherService } from '../../teacher.service';
+import { MatDialog } from '@angular/material/dialog';
+import { UnlinkGoogleAccountConfirmComponent } from '../../../modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component';
+import { Subscription } from 'rxjs';
@Component({
selector: 'app-edit-profile',
templateUrl: './edit-profile.component.html',
styleUrls: ['./edit-profile.component.scss']
})
-export class EditProfileComponent implements OnInit {
+export class EditProfileComponent {
user: Teacher;
schoolLevels: any[] = [
{ id: 'ELEMENTARY_SCHOOL', label: $localize`Elementary School` },
@@ -23,6 +26,8 @@ export class EditProfileComponent implements OnInit {
languages: object[];
changed: boolean = false;
isSaving: boolean = false;
+ isGoogleUser: boolean = false;
+ userSubscription: Subscription;
editProfileFormGroup: FormGroup = this.fb.group({
firstName: new FormControl({ value: '', disabled: true }, [Validators.required]),
@@ -41,6 +46,7 @@ export class EditProfileComponent implements OnInit {
private fb: FormBuilder,
private teacherService: TeacherService,
private userService: UserService,
+ public dialog: MatDialog,
public snackBar: MatSnackBar
) {
this.user =
this.getUser().getValue();
@@ -57,10 +63,6 @@ export class EditProfileComponent implements OnInit {
this.userService.getLanguages().subscribe((response) => {
this.languages = response;
});
-
- this.editProfileFormGroup.valueChanges.subscribe(() => {
- this.changed = true;
- });
}
getUser() {
@@ -71,7 +73,19 @@ export class EditProfileComponent implements OnInit {
this.editProfileFormGroup.controls[name].setValue(value);
}
- ngOnInit() {}
+ ngOnInit() {
+ this.editProfileFormGroup.valueChanges.subscribe(() => {
+ this.changed = true;
+ });
+
+ this.userSubscription = this.userService.getUser().subscribe((user) => {
+ this.isGoogleUser = user.isGoogleUser;
+ });
+ }
+
+ ngOnDestroy() {
+ this.userSubscription.unsubscribe();
+ }
saveChanges() {
this.isSaving = true;
diff --git a/src/main/webapp/site/src/messages.xlf b/src/main/webapp/site/src/messages.xlf
index e6b1586225..1feee2c605 100644
--- a/src/main/webapp/site/src/messages.xlf
+++ b/src/main/webapp/site/src/messages.xlf
@@ -236,29 +236,87 @@
10
-
- Current Password
+
+ Unlink Google Account
+
+ app/modules/shared/unlink-google-account-success/unlink-google-account-success.component.html
+ 1
+
+
+ app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.html
+ 1
+
+
+ app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.html
+ 1
+
app/modules/shared/edit-password/edit-password.component.html
- 8
+ 66
+
+
+ app/student/account/edit-profile/edit-profile.component.html
+ 64
+
+
+ app/teacher/account/edit-profile/edit-profile.component.html
+ 131
-
- Current Password required
+
+ Success! You have unlinked your Google account from WISE. To sign in to WISE in the future, please use your username and password you just created.
- app/modules/shared/edit-password/edit-password.component.html
- 15
+ app/modules/shared/unlink-google-account-success/unlink-google-account-success.component.html
+ 4
-
- Current Password is incorrect
+
+ Your username is:
- app/modules/shared/edit-password/edit-password.component.html
- 16
+ app/modules/shared/unlink-google-account-success/unlink-google-account-success.component.html
+ 7
+
+
+
+ Done
+
+ app/modules/shared/unlink-google-account-success/unlink-google-account-success.component.html
+ 11
+
+
+ app/teacher/list-classroom-courses-dialog/list-classroom-courses-dialog.component.html
+ 75
+
+
+ app/teacher/create-run-dialog/create-run-dialog.component.html
+ 85
+
+
+ app/modules/library/share-project-dialog/share-project-dialog.component.html
+ 70
+
+
+ app/teacher/share-run-dialog/share-run-dialog.component.html
+ 102
+
+
+ app/teacher/run-settings-dialog/run-settings-dialog.component.html
+ 76
+
+
+
+ Create a WISE password:
+
+ app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.html
+ 4
New Password
+
+ app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.html
+ 8
+
app/modules/shared/edit-password/edit-password.component.html
22
@@ -266,6 +324,10 @@
New Password required
+
+ app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.html
+ 15
+
app/modules/shared/edit-password/edit-password.component.html
29
@@ -273,6 +335,10 @@
Confirm New Password
+
+ app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.html
+ 20
+
app/modules/shared/edit-password/edit-password.component.html
34
@@ -280,6 +346,10 @@
Confirm Password required
+
+ app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.html
+ 27
+
app/modules/shared/edit-password/edit-password.component.html
41
@@ -303,11 +373,155 @@
Passwords do not match
+
+ app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.html
+ 28
+
app/modules/shared/edit-password/edit-password.component.html
42
+
+ Cancel
+
+ app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.html
+ 35
+
+
+ app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.html
+ 11
+
+
+ app/modules/library/copy-project-dialog/copy-project-dialog.component.html
+ 11
+
+
+ app/teacher/list-classroom-courses-dialog/list-classroom-courses-dialog.component.html
+ 47
+
+
+ app/teacher/create-run-dialog/create-run-dialog.component.html
+ 63
+
+
+ app/teacher/use-with-class-warning-dialog/use-with-class-warning-dialog.component.html
+ 15
+
+
+ app/teacher/edit-run-warning-dialog/edit-run-warning-dialog.component.html
+ 20
+
+
+ app/student/add-project-dialog/add-project-dialog.component.html
+ 23
+
+
+ app/student/team-sign-in-dialog/team-sign-in-dialog.component.html
+ 68
+
+
+ app/teacher/share-run-dialog/share-run-dialog.component.html
+ 100
+
+
+ app/authoring-tool/import-step/choose-import-step/choose-import-step.component.html
+ 62
+
+
+ app/authoring-tool/import-step/choose-import-step-location/choose-import-step-location.component.html
+ 40
+
+
+ app/authoring-tool/add-component/choose-new-component/choose-new-component.component.html
+ 23
+
+
+ app/authoring-tool/add-component/choose-new-component-location/choose-new-component-location.component.html
+ 41
+
+
+
+ Submit
+
+ app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.html
+ 43
+
+
+ app/contact/contact-form/contact-form.component.html
+ 112
+
+
+ app/forgot/student/forgot-student-password/forgot-student-password.component.html
+ 25
+
+
+ app/forgot/teacher/forgot-teacher-username/forgot-teacher-username.component.html
+ 25
+
+
+ app/forgot/teacher/forgot-teacher-password/forgot-teacher-password.component.html
+ 25
+
+
+ app/forgot/student/forgot-student-password-security/forgot-student-password-security.component.html
+ 28
+
+
+ app/forgot/student/forgot-student-password-change/forgot-student-password-change.component.html
+ 41
+
+
+ app/forgot/teacher/forgot-teacher-password-change/forgot-teacher-password-change.component.html
+ 39
+
+
+ app/forgot/teacher/forgot-teacher-password-verify/forgot-teacher-password-verify.component.html
+ 26
+
+
+
+ If you remove the link to your Google account, you will be asked to create a WISE password. You will then need to sign in to WISE using your username and password in the future.
+
+ app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.html
+ 4
+
+
+
+ Note: You will no longer be able to sign in to this WISE account using Google. Do you want to continue?
+
+ app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.html
+ 7
+
+
+
+ Continue
+
+ app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.html
+ 15
+
+
+
+ Current Password
+
+ app/modules/shared/edit-password/edit-password.component.html
+ 8
+
+
+
+ Current Password required
+
+ app/modules/shared/edit-password/edit-password.component.html
+ 15
+
+
+
+ Current Password is incorrect
+
+ app/modules/shared/edit-password/edit-password.component.html
+ 16
+
+
Change Password
@@ -323,8 +537,8 @@
7
-
- This account was created using Google and doesn't use a WISE password. If you would like to unlink your Google account, please contact us .
+
+ This account was created using Google and doesn't use a WISE password.
app/modules/shared/edit-password/edit-password.component.html
60
@@ -1068,57 +1282,6 @@
7
-
- Cancel
-
- app/modules/library/copy-project-dialog/copy-project-dialog.component.html
- 11
-
-
- app/teacher/list-classroom-courses-dialog/list-classroom-courses-dialog.component.html
- 47
-
-
- app/teacher/create-run-dialog/create-run-dialog.component.html
- 63
-
-
- app/teacher/use-with-class-warning-dialog/use-with-class-warning-dialog.component.html
- 15
-
-
- app/teacher/edit-run-warning-dialog/edit-run-warning-dialog.component.html
- 20
-
-
- app/student/add-project-dialog/add-project-dialog.component.html
- 23
-
-
- app/student/team-sign-in-dialog/team-sign-in-dialog.component.html
- 68
-
-
- app/teacher/share-run-dialog/share-run-dialog.component.html
- 100
-
-
- app/authoring-tool/import-step/choose-import-step/choose-import-step.component.html
- 62
-
-
- app/authoring-tool/import-step/choose-import-step-location/choose-import-step-location.component.html
- 40
-
-
- app/authoring-tool/add-component/choose-new-component/choose-new-component.component.html
- 23
-
-
- app/authoring-tool/add-component/choose-new-component-location/choose-new-component-location.component.html
- 41
-
-
Copy
@@ -1244,29 +1407,6 @@
69
-
- Done
-
- app/teacher/list-classroom-courses-dialog/list-classroom-courses-dialog.component.html
- 75
-
-
- app/teacher/create-run-dialog/create-run-dialog.component.html
- 85
-
-
- app/modules/library/share-project-dialog/share-project-dialog.component.html
- 70
-
-
- app/teacher/share-run-dialog/share-run-dialog.component.html
- 102
-
-
- app/teacher/run-settings-dialog/run-settings-dialog.component.html
- 76
-
-
Use with Class
@@ -2282,41 +2422,6 @@
104
-
- Submit
-
- app/contact/contact-form/contact-form.component.html
- 112
-
-
- app/forgot/student/forgot-student-password/forgot-student-password.component.html
- 25
-
-
- app/forgot/teacher/forgot-teacher-username/forgot-teacher-username.component.html
- 25
-
-
- app/forgot/teacher/forgot-teacher-password/forgot-teacher-password.component.html
- 25
-
-
- app/forgot/student/forgot-student-password-security/forgot-student-password-security.component.html
- 28
-
-
- app/forgot/student/forgot-student-password-change/forgot-student-password-change.component.html
- 41
-
-
- app/forgot/teacher/forgot-teacher-password-change/forgot-teacher-password-change.component.html
- 39
-
-
- app/forgot/teacher/forgot-teacher-password-verify/forgot-teacher-password-verify.component.html
- 26
-
-
WISE Features
@@ -5287,11 +5392,22 @@
Save Changes
app/student/account/edit-profile/edit-profile.component.html
- 53
+ 55
app/teacher/account/edit-profile/edit-profile.component.html
- 120
+ 122
+
+
+
+ This profile is linked to a Google account.
+
+ app/student/account/edit-profile/edit-profile.component.html
+ 58
+
+
+ app/teacher/account/edit-profile/edit-profile.component.html
+ 125
diff --git a/src/test/java/org/wise/portal/presentation/web/controllers/user/GoogleUserAPIControllerTest.java b/src/test/java/org/wise/portal/presentation/web/controllers/user/GoogleUserAPIControllerTest.java
new file mode 100644
index 0000000000..b66900f8ff
--- /dev/null
+++ b/src/test/java/org/wise/portal/presentation/web/controllers/user/GoogleUserAPIControllerTest.java
@@ -0,0 +1,108 @@
+package org.wise.portal.presentation.web.controllers.user;
+
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.HashMap;
+
+import org.easymock.TestSubject;
+import org.junit.Test;
+import org.wise.portal.presentation.web.exception.InvalidPasswordExcpetion;
+
+public class GoogleUserAPIControllerTest extends UserAPIControllerTest {
+
+ @TestSubject
+ private GoogleUserAPIController controller = new GoogleUserAPIController();
+
+ @Test
+ public void isGoogleIdExist_GoogleUserExists_ReturnTrue() {
+ expect(userService.retrieveUserByGoogleUserId(STUDENT1_GOOGLE_ID)).andReturn(student1);
+ replay(userService);
+ assertTrue(controller.isGoogleIdExist(STUDENT1_GOOGLE_ID));
+ verify(userService);
+ }
+
+ @Test
+ public void isGoogleIdExist_InvalidGoogleUserId_ReturnFalse() {
+ String invalidGoogleId = "google-id-not-exists-in-db";
+ expect(userService.retrieveUserByGoogleUserId(invalidGoogleId)).andReturn(null);
+ replay(userService);
+ assertFalse(controller.isGoogleIdExist(invalidGoogleId));
+ verify(userService);
+ }
+
+ @Test
+ public void isGoogleIdMatches_GoogleUserIdAndUserIdMatch_ReturnTrue() {
+ expect(userService.retrieveUserByGoogleUserId(STUDENT1_GOOGLE_ID)).andReturn(student1);
+ replay(userService);
+ assertTrue(controller.isGoogleIdMatches(STUDENT1_GOOGLE_ID, student1Id.toString()));
+ verify(userService);
+ }
+
+ @Test
+ public void isGoogleIdMatches_InvalidGoogleUserId_ReturnFalse() {
+ String invalidGoogleId = "google-id-not-exists-in-db";
+ expect(userService.retrieveUserByGoogleUserId(invalidGoogleId)).andReturn(null);
+ replay(userService);
+ assertFalse(controller.isGoogleIdMatches(invalidGoogleId, student1Id.toString()));
+ verify(userService);
+ }
+
+ @Test
+ public void isGoogleIdMatches_GoogleUserIdAndUserIdDoNotMatch_ReturnFalse() {
+ expect(userService.retrieveUserByGoogleUserId(STUDENT1_GOOGLE_ID)).andReturn(teacher1);
+ replay(userService);
+ assertFalse(controller.isGoogleIdMatches(STUDENT1_GOOGLE_ID, teacher1.toString()));
+ verify(userService);
+ }
+
+ @Test
+ public void getUserByGoogleId_GoogleUserExists_ReturnSuccessResponse() {
+ expect(userService.retrieveUserByGoogleUserId(STUDENT1_GOOGLE_ID)).andReturn(student1);
+ replay(userService);
+ HashMap response = controller.getUserByGoogleId(STUDENT1_GOOGLE_ID);
+ assertEquals("success", response.get("status"));
+ assertEquals(student1.getId(), response.get("userId"));
+ verify(userService);
+ }
+
+ @Test
+ public void getUserByGoogleId_InvalidGoogleUserId_ReturnErrorResponse() {
+ String invalidGoogleId = "google-id-not-exists-in-db";
+ expect(userService.retrieveUserByGoogleUserId(invalidGoogleId)).andReturn(null);
+ replay(userService);
+ HashMap response = controller.getUserByGoogleId(invalidGoogleId);
+ assertEquals("error", response.get("status"));
+ verify(userService);
+ }
+
+ @Test
+ public void unlinkGoogleAccount_InvalidNewPassword_ThrowException() {
+ expect(userService.retrieveUserByUsername(STUDENT_USERNAME)).andReturn(student1);
+ replay(userService);
+ String newPass = "";
+ try {
+ controller.unlinkGoogleAccount(studentAuth, newPass);
+ fail("InvalidPasswordException was expected");
+ } catch (Exception e) {
+ }
+ }
+
+ @Test
+ public void unlinkGoogleAccount_ValidNewPassword_ReturnUpdatedUserMap()
+ throws InvalidPasswordExcpetion {
+ String newPassword = "my new pass";
+ assertTrue(student1.getUserDetails().isGoogleUser());
+ expect(userService.retrieveUserByUsername(STUDENT_USERNAME)).andReturn(student1).times(2);
+ expect(userService.updateUserPassword(student1, newPassword)).andReturn(student1);
+ expect(appProperties.getProperty("send_email_enabled", "false")).andReturn("false");
+ replay(userService, appProperties);
+ controller.unlinkGoogleAccount(studentAuth, newPassword);
+ verify(userService, appProperties);
+ }
+}
diff --git a/src/test/java/org/wise/portal/presentation/web/controllers/user/UserAPIControllerTest.java b/src/test/java/org/wise/portal/presentation/web/controllers/user/UserAPIControllerTest.java
index 251fcb0b47..c5b901ce8f 100644
--- a/src/test/java/org/wise/portal/presentation/web/controllers/user/UserAPIControllerTest.java
+++ b/src/test/java/org/wise/portal/presentation/web/controllers/user/UserAPIControllerTest.java
@@ -143,68 +143,6 @@ public void getSupportedLanguages_ThreeSupportedLocales_ReturnLanguageArray() {
verify(appProperties);
}
- @Test
- public void isGoogleIdExist_GoogleUserExists_ReturnTrue() {
- expect(userService.retrieveUserByGoogleUserId(STUDENT1_GOOGLE_ID)).andReturn(student1);
- replay(userService);
- assertTrue(userAPIController.isGoogleIdExist(STUDENT1_GOOGLE_ID));
- verify(userService);
- }
-
- @Test
- public void isGoogleIdExist_InvalidGoogleUserId_ReturnFalse() {
- String invalidGoogleId = "google-id-not-exists-in-db";
- expect(userService.retrieveUserByGoogleUserId(invalidGoogleId)).andReturn(null);
- replay(userService);
- assertFalse(userAPIController.isGoogleIdExist(invalidGoogleId));
- verify(userService);
- }
-
- @Test
- public void isGoogleIdMatches_GoogleUserIdAndUserIdMatch_ReturnTrue() {
- expect(userService.retrieveUserByGoogleUserId(STUDENT1_GOOGLE_ID)).andReturn(student1);
- replay(userService);
- assertTrue(userAPIController.isGoogleIdMatches(STUDENT1_GOOGLE_ID, student1Id.toString()));
- verify(userService);
- }
-
- @Test
- public void isGoogleIdMatches_InvalidGoogleUserId_ReturnFalse() {
- String invalidGoogleId = "google-id-not-exists-in-db";
- expect(userService.retrieveUserByGoogleUserId(invalidGoogleId)).andReturn(null);
- replay(userService);
- assertFalse(userAPIController.isGoogleIdMatches(invalidGoogleId, student1Id.toString()));
- verify(userService);
- }
-
- @Test
- public void isGoogleIdMatches_GoogleUserIdAndUserIdDoNotMatch_ReturnFalse() {
- expect(userService.retrieveUserByGoogleUserId(STUDENT1_GOOGLE_ID)).andReturn(teacher1);
- replay(userService);
- assertFalse(userAPIController.isGoogleIdMatches(STUDENT1_GOOGLE_ID, teacher1.toString()));
- verify(userService);
- }
-
- @Test
- public void getUserByGoogleId_GoogleUserExists_ReturnSuccessResponse() {
- expect(userService.retrieveUserByGoogleUserId(STUDENT1_GOOGLE_ID)).andReturn(student1);
- replay(userService);
- HashMap response = userAPIController.getUserByGoogleId(STUDENT1_GOOGLE_ID);
- assertEquals("success", response.get("status"));
- assertEquals(student1.getId(), response.get("userId"));
- verify(userService);
- }
-
- @Test
- public void getUserByGoogleId_InvalidGoogleUserId_ReturnErrorResponse() {
- String invalidGoogleId = "google-id-not-exists-in-db";
- expect(userService.retrieveUserByGoogleUserId(invalidGoogleId)).andReturn(null);
- replay(userService);
- HashMap response = userAPIController.getUserByGoogleId(invalidGoogleId);
- assertEquals("error", response.get("status"));
- verify(userService);
- }
-
@Test
public void isNameValid_InvalidName_ReturnFalse() {
assertFalse(userAPIController.isNameValid(""));
From 6f28ba9bf0942ee017e3e39588c3828b12e66714 Mon Sep 17 00:00:00 2001
From: Hiroki Terashima
Date: Tue, 26 Jan 2021 09:24:00 -0800
Subject: [PATCH 2/3] Aded unlinkGoogleAccount() to teacher edit profile. #2870
---
.../student/account/edit-profile/edit-profile.component.ts | 2 +-
.../teacher/account/edit-profile/edit-profile.component.ts | 6 +++++-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.ts b/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.ts
index afe3d42111..f083a25656 100644
--- a/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.ts
+++ b/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.ts
@@ -1,4 +1,4 @@
-import { Component, OnInit } from '@angular/core';
+import { Component } from '@angular/core';
import { FormGroup, FormControl, Validators, FormBuilder } from '@angular/forms';
import { finalize } from 'rxjs/operators';
import { MatSnackBar } from '@angular/material/snack-bar';
diff --git a/src/main/webapp/site/src/app/teacher/account/edit-profile/edit-profile.component.ts b/src/main/webapp/site/src/app/teacher/account/edit-profile/edit-profile.component.ts
index a1c09fd71f..a6e0d95779 100644
--- a/src/main/webapp/site/src/app/teacher/account/edit-profile/edit-profile.component.ts
+++ b/src/main/webapp/site/src/app/teacher/account/edit-profile/edit-profile.component.ts
@@ -1,4 +1,4 @@
-import { Component, OnInit } from '@angular/core';
+import { Component } from '@angular/core';
import { FormControl, FormGroup, Validators, FormBuilder } from '@angular/forms';
import { finalize } from 'rxjs/operators';
import { MatSnackBar } from '@angular/material/snack-bar';
@@ -142,4 +142,8 @@ export class EditProfileComponent {
this.snackBar.open($localize`An error occurred. Please try again.`);
}
}
+
+ unlinkGoogleAccount() {
+ this.dialog.open(UnlinkGoogleAccountConfirmComponent);
+ }
}
From ffd530cb511d3e3609b9464bbf6b0b26b05941d8 Mon Sep 17 00:00:00 2001
From: breity
Date: Fri, 29 Jan 2021 13:27:06 -0800
Subject: [PATCH 3/3] Styled and updated text for unlink Google account
dialogs, student and teacher profile pages. #2870
---
.../edit-password.component.html | 18 +-
.../edit-password.component.scss | 9 +-
.../edit-password/edit-password.component.ts | 4 +-
...link-google-account-confirm.component.html | 23 ++-
...link-google-account-confirm.component.scss | 7 +-
...unlink-google-account-confirm.component.ts | 4 +-
...ink-google-account-password.component.html | 87 ++++-----
...ink-google-account-password.component.scss | 5 +-
...nlink-google-account-password.component.ts | 4 +-
...link-google-account-success.component.html | 11 +-
...link-google-account-success.component.scss | 5 +-
.../edit-profile/edit-profile.component.html | 43 +++--
.../edit-profile/edit-profile.component.scss | 17 +-
.../edit-profile/edit-profile.component.ts | 4 +-
.../edit-profile/edit-profile.component.html | 37 ++--
.../edit-profile/edit-profile.component.scss | 13 ++
.../edit-profile/edit-profile.component.ts | 4 +-
.../edit-run-warning-dialog.component.html | 4 +-
src/main/webapp/site/src/messages.xlf | 177 +++++++++++-------
.../site/src/style/layout/_section.scss | 2 +-
20 files changed, 280 insertions(+), 198 deletions(-)
diff --git a/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.html b/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.html
index 71af1f2b7d..e2fd57a2df 100644
--- a/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.html
+++ b/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.html
@@ -57,13 +57,17 @@
- This account was created using Google and doesn't use a WISE password.
-
-
- link_off
- Unlink Google Account
+
+
+ This account was created using Google and doesn't use a WISE password.
+
+
+
+ Unlink Google Account
diff --git a/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.scss b/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.scss
index 940482c43e..60fab472cc 100644
--- a/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.scss
+++ b/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.scss
@@ -11,6 +11,11 @@ form {
}
}
-.notice {
- margin: 0 auto;
+.google-icon {
+ height: 1.8em;
+ width: auto;
+}
+
+.unlink {
+ margin: 8px 0;
}
diff --git a/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.ts b/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.ts
index 8df43f729b..7539bc803e 100644
--- a/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.ts
+++ b/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.ts
@@ -83,7 +83,9 @@ export class EditPasswordComponent {
}
unlinkGoogleAccount() {
- this.dialog.open(UnlinkGoogleAccountConfirmComponent);
+ this.dialog.open(UnlinkGoogleAccountConfirmComponent, {
+ panelClass: 'mat-dialog--sm'
+ });
}
resetForm() {
diff --git a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.html b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.html
index 1787a19ea9..9fd9a47c13 100644
--- a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.html
+++ b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.html
@@ -1,17 +1,16 @@
-Unlink Google Account
+
+
+ Unlink Google Account
+
+ warning
+
-
- If you remove the link to your Google account, you will be asked to create a WISE password. You will then need to sign in to WISE using your username and password in the future.
-
-
Note: You will no longer be able to sign in to this WISE account using Google. Do you want to continue?
+
To remove the link to your Google account, you will be asked to create a WISE password. In the future, you'll sign in to WISE using your username and password.
+
You will no longer be able to sign in to WISE using Google. Would you like to continue?
-
- Cancel
-
- Continue
-
+
+ Cancel
+ Continue
diff --git a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.scss b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.scss
index 0af6d8c18e..257ab6ea45 100644
--- a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.scss
+++ b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.scss
@@ -1,3 +1,4 @@
-.info-note {
- font-weight: bold;
-}
+.google-icon {
+ height: 1.4em;
+ width: auto;
+}
\ No newline at end of file
diff --git a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.ts b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.ts
index 73a3e6d7f5..99a4fe2fd8 100644
--- a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.ts
+++ b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-confirm/unlink-google-account-confirm.component.ts
@@ -11,6 +11,8 @@ export class UnlinkGoogleAccountConfirmComponent {
continue() {
this.dialog.closeAll();
- this.dialog.open(UnlinkGoogleAccountPasswordComponent);
+ this.dialog.open(UnlinkGoogleAccountPasswordComponent, {
+ panelClass: 'mat-dialog--sm'
+ });
}
}
diff --git a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.html b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.html
index 88f3989891..e5c496120f 100644
--- a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.html
+++ b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.html
@@ -1,46 +1,41 @@
-Unlink Google Account
-
-
-
Create a WISE password:
-
-
-
-
- Cancel
-
- Submit
-
-
-
+
+
+ Unlink Google Account
+
+
diff --git a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.scss b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.scss
index efd343d97f..6629b7fe81 100644
--- a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.scss
+++ b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.scss
@@ -1,3 +1,4 @@
-mat-dialog-content {
- min-width: 600px;
+.google-icon {
+ height: 1.4em;
+ width: auto;
}
diff --git a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.ts b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.ts
index 152b168f88..da0a00f3a0 100644
--- a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.ts
+++ b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-password/unlink-google-account-password.component.ts
@@ -32,7 +32,9 @@ export class UnlinkGoogleAccountPasswordComponent {
.add(() => {
this.isSaving = false;
this.dialog.closeAll();
- this.dialog.open(UnlinkGoogleAccountSuccessComponent);
+ this.dialog.open(UnlinkGoogleAccountSuccessComponent, {
+ panelClass: 'mat-dialog--sm'
+ });
});
}
}
diff --git a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-success/unlink-google-account-success.component.html b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-success/unlink-google-account-success.component.html
index e985c5bb42..c9d91706f6 100644
--- a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-success/unlink-google-account-success.component.html
+++ b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-success/unlink-google-account-success.component.html
@@ -1,10 +1,11 @@
-Unlink Google Account
+
+
+ Unlink Google Account
+
-
- Success! You have unlinked your Google account from WISE. To sign in to WISE in the future, please use your username and password you just created.
-
-
Your username is: {{username}}
+
Success! You have unlinked your Google account from WISE. To sign in to WISE in the future, please use your username and the password you just created.
+
Your username is: {{ username }} .
diff --git a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-success/unlink-google-account-success.component.scss b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-success/unlink-google-account-success.component.scss
index 0af6d8c18e..6629b7fe81 100644
--- a/src/main/webapp/site/src/app/modules/shared/unlink-google-account-success/unlink-google-account-success.component.scss
+++ b/src/main/webapp/site/src/app/modules/shared/unlink-google-account-success/unlink-google-account-success.component.scss
@@ -1,3 +1,4 @@
-.info-note {
- font-weight: bold;
+.google-icon {
+ height: 1.4em;
+ width: auto;
}
diff --git a/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.html b/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.html
index 14fe2c4609..10dd31c105 100644
--- a/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.html
+++ b/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.html
@@ -1,5 +1,5 @@