From ab67d1bd34771f59fe1d38ab2ec18018bcb7d84f Mon Sep 17 00:00:00 2001
From: Geoffrey Kwan
Date: Fri, 22 Jan 2021 19:12:46 -0500
Subject: [PATCH 01/13] Extracted MilestoneDetailsDialog into its own ts and
html files. #2897
---
.../milestoneDetailsDialog.html | 21 ++++
.../milestoneDetailsDialog.ts | 77 +++++++++++++
.../webapp/wise5/services/milestoneService.ts | 105 +-----------------
3 files changed, 104 insertions(+), 99 deletions(-)
create mode 100644 src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/milestones/milestoneDetailsDialog/milestoneDetailsDialog.html
create mode 100644 src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/milestones/milestoneDetailsDialog/milestoneDetailsDialog.ts
diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/milestones/milestoneDetailsDialog/milestoneDetailsDialog.html b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/milestones/milestoneDetailsDialog/milestoneDetailsDialog.html
new file mode 100644
index 0000000000..986833bfc1
--- /dev/null
+++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/milestones/milestoneDetailsDialog/milestoneDetailsDialog.html
@@ -0,0 +1,21 @@
+
+
+
+
{{ 'MILESTONE_DETAILS_TITLE' | translate : { name: $ctrl.milestone.name } }}
+
+
+
+
+
+
+
+
+ {{ ::'CLOSE' | translate }}
+
+
+
\ No newline at end of file
diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/milestones/milestoneDetailsDialog/milestoneDetailsDialog.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/milestones/milestoneDetailsDialog/milestoneDetailsDialog.ts
new file mode 100644
index 0000000000..eaec3e2d0b
--- /dev/null
+++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/milestones/milestoneDetailsDialog/milestoneDetailsDialog.ts
@@ -0,0 +1,77 @@
+export class MilestoneDetailsDialog {
+ title: string;
+
+ static $inject = [
+ '$state',
+ '$mdDialog',
+ '$event',
+ 'milestone',
+ 'hideStudentWork',
+ 'TeacherDataService'
+ ];
+
+ constructor(
+ private $state,
+ private $mdDialog,
+ private $event,
+ private milestone,
+ private hideStudentWork,
+ private TeacherDataService
+ ) {}
+
+ $onInit() {
+ this.saveMilestoneOpenedEvent();
+ }
+
+ close() {
+ this.saveMilestoneClosedEvent();
+ this.$mdDialog.hide();
+ }
+
+ edit() {
+ this.$mdDialog.hide({
+ milestone: this.milestone,
+ action: 'edit',
+ $event: this.$event
+ });
+ }
+
+ onShowWorkgroup(workgroup: any) {
+ this.saveMilestoneClosedEvent();
+ this.$mdDialog.hide();
+ this.TeacherDataService.setCurrentWorkgroup(workgroup);
+ this.$state.go('root.nodeProgress');
+ }
+
+ onVisitNodeGrading() {
+ this.$mdDialog.hide();
+ }
+
+ saveMilestoneOpenedEvent() {
+ this.saveMilestoneEvent('MilestoneOpened');
+ }
+
+ saveMilestoneClosedEvent() {
+ this.saveMilestoneEvent('MilestoneClosed');
+ }
+
+ saveMilestoneEvent(event: any) {
+ const context = 'ClassroomMonitor',
+ nodeId = null,
+ componentId = null,
+ componentType = null,
+ category = 'Navigation',
+ data = { milestoneId: this.milestone.id },
+ projectId = null;
+ this.TeacherDataService.saveEvent(
+ context,
+ nodeId,
+ componentId,
+ componentType,
+ category,
+ event,
+ data,
+ projectId
+ );
+ }
+}
diff --git a/src/main/webapp/wise5/services/milestoneService.ts b/src/main/webapp/wise5/services/milestoneService.ts
index 47580d6e77..561e31ed79 100644
--- a/src/main/webapp/wise5/services/milestoneService.ts
+++ b/src/main/webapp/wise5/services/milestoneService.ts
@@ -9,6 +9,7 @@ import { TeacherDataService } from './teacherDataService';
import { UtilService } from './utilService';
import { Injectable } from '@angular/core';
import { UpgradeModule } from '@angular/upgrade/static';
+import { MilestoneDetailsDialog } from '../classroomMonitor/classroomMonitorComponents/milestones/milestoneDetailsDialog/milestoneDetailsDialog';
@Injectable()
export class MilestoneService {
@@ -511,40 +512,12 @@ export class MilestoneService {
}
showMilestoneDetails(milestone: any, $event: any, hideStudentWork: boolean = false) {
- const title = this.getTranslation('MILESTONE_DETAILS_TITLE', {
- name: milestone.name
- });
- const template = `
-
-
-
${title}
-
-
-
-
-
-
-
-
- {{ ::'EDIT' | translate }}
-
-
- {{ ::'CLOSE' | translate }}
-
-
- `;
this.upgrade.$injector.get('$mdDialog').show({
parent: angular.element(document.body),
- template: template,
- ariaLabel: title,
+ templateUrl:
+ 'wise5/classroomMonitor/classroomMonitorComponents/milestones/milestoneDetailsDialog/milestoneDetailsDialog.html',
+ controller: MilestoneDetailsDialog,
+ controllerAs: '$ctrl',
fullscreen: true,
multiple: true,
targetEvent: $event,
@@ -554,73 +527,7 @@ export class MilestoneService {
$event: $event,
milestone: milestone,
hideStudentWork: hideStudentWork
- },
- controller: [
- '$scope',
- '$state',
- '$mdDialog',
- 'milestone',
- '$event',
- 'TeacherDataService',
- function DialogController(
- $scope,
- $state,
- $mdDialog,
- milestone,
- $event,
- TeacherDataService
- ) {
- $scope.milestone = milestone;
- $scope.hideStudentWork = hideStudentWork;
- $scope.event = $event;
- $scope.close = function () {
- $scope.saveMilestoneClosedEvent();
- $mdDialog.hide();
- };
- $scope.edit = function () {
- $mdDialog.hide({
- milestone: $scope.milestone,
- action: 'edit',
- $event: $event
- });
- };
- $scope.onShowWorkgroup = function (workgroup: any) {
- $scope.saveMilestoneClosedEvent();
- $mdDialog.hide();
- TeacherDataService.setCurrentWorkgroup(workgroup);
- $state.go('root.nodeProgress');
- };
- $scope.onVisitNodeGrading = function () {
- $mdDialog.hide();
- };
- $scope.saveMilestoneOpenedEvent = function () {
- $scope.saveMilestoneEvent('MilestoneOpened');
- };
- $scope.saveMilestoneClosedEvent = function () {
- $scope.saveMilestoneEvent('MilestoneClosed');
- };
- $scope.saveMilestoneEvent = function (event: any) {
- const context = 'ClassroomMonitor',
- nodeId = null,
- componentId = null,
- componentType = null,
- category = 'Navigation',
- data = { milestoneId: $scope.milestone.id },
- projectId = null;
- TeacherDataService.saveEvent(
- context,
- nodeId,
- componentId,
- componentType,
- category,
- event,
- data,
- projectId
- );
- };
- $scope.saveMilestoneOpenedEvent();
- }
- ]
+ }
});
}
}
From 87ffdee0e56f8a23b93b76e673f733fe58be33a1 Mon Sep 17 00:00:00 2001
From: Geoffrey Kwan
Date: Mon, 25 Jan 2021 12:15:24 -0500
Subject: [PATCH 02/13] Upgraded Label authoring to Angular. #2895
---
.../src/app/teacher-hybrid-angular.module.ts | 2 +
.../component-authoring.component.ts | 5 +
.../wise5/components/label/authoring.html | 373 ------------------
.../label-authoring.component.html | 236 +++++++++++
.../label-authoring.component.scss | 64 +++
.../label-authoring.component.ts | 134 +++++++
.../wise5/components/label/labelAuthoring.ts | 177 ---------
.../label/labelAuthoringComponentModule.ts | 9 +-
8 files changed, 447 insertions(+), 553 deletions(-)
delete mode 100644 src/main/webapp/wise5/components/label/authoring.html
create mode 100644 src/main/webapp/wise5/components/label/label-authoring/label-authoring.component.html
create mode 100644 src/main/webapp/wise5/components/label/label-authoring/label-authoring.component.scss
create mode 100644 src/main/webapp/wise5/components/label/label-authoring/label-authoring.component.ts
delete mode 100644 src/main/webapp/wise5/components/label/labelAuthoring.ts
diff --git a/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts b/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts
index e9a9ff545a..6a5134318d 100644
--- a/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts
+++ b/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts
@@ -51,6 +51,7 @@ import { MultipleChoiceAuthoring } from '../../../wise5/components/multipleChoic
import { ConceptMapAuthoring } from '../../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component';
import { DrawAuthoring } from '../../../wise5/components/draw/draw-authoring/draw-authoring.component';
import { MatchAuthoring } from '../../../wise5/components/match/match-authoring/match-authoring.component';
+import { LabelAuthoring } from '../../../wise5/components/label/label-authoring/label-authoring.component';
@NgModule({
declarations: [
@@ -72,6 +73,7 @@ import { MatchAuthoring } from '../../../wise5/components/match/match-authoring/
EditHTMLAdvancedComponent,
EditOutsideUrlAdvancedComponent,
HtmlAuthoring,
+ LabelAuthoring,
ManageStudentsComponent,
MatchAuthoring,
MilestonesComponent,
diff --git a/src/main/webapp/wise5/authoringTool/components/component-authoring.component.ts b/src/main/webapp/wise5/authoringTool/components/component-authoring.component.ts
index 6634abd0cc..225b94249e 100644
--- a/src/main/webapp/wise5/authoringTool/components/component-authoring.component.ts
+++ b/src/main/webapp/wise5/authoringTool/components/component-authoring.component.ts
@@ -62,6 +62,11 @@ export abstract class ComponentAuthoring {
);
}
+ ngOnDestroy() {
+ this.componentChangedSubscription.unsubscribe();
+ this.starterStateResponseSubscription.unsubscribe();
+ }
+
promptChanged(prompt: string): void {
this.promptChange.next(prompt);
}
diff --git a/src/main/webapp/wise5/components/label/authoring.html b/src/main/webapp/wise5/components/label/authoring.html
deleted file mode 100644
index 2e53f91e8a..0000000000
--- a/src/main/webapp/wise5/components/label/authoring.html
+++ /dev/null
@@ -1,373 +0,0 @@
-
-
-
-
-
-
- {{ ::'SHOW_SAVE_BUTTON' | translate }}
-
-
-
-
- {{ ::'SHOW_SUBMIT_BUTTON' | translate }}
-
-
-
-
- {{ ::'SHOW_ADD_TO_NOTEBOOK_BUTTON' | translate }}
-
-
-
-
- {{ ::'MAX_SUBMIT' | translate }}
-
-
-
-
-
-
-
-
-
-
-
-
- {{ ::'CONNECTED_COMPONENTS' | translate }}
-
-
- add
-
- {{ ::'ADD_CONNECTED_COMPONENT' | translate }}
-
-
-
-
-
-
- {{ ::'step' | translate }}
-
-
- {{ labelController.getNodePositionAndTitleByNodeId(item.$key) }}
-
-
-
-
- {{ ::'component' | translate }}
-
-
- {{ componentIndex + 1 }}. {{ component.type }}
-
- ({{ ::'thisComponent' | translate }})
-
-
-
-
-
- {{ ::'type' | translate }}
-
-
- {{ ::'importWork' | translate }}
-
-
- {{ ::'showWork' | translate }}
-
-
-
-
-
-
- delete
-
- {{ ::'DELETE' | translate }}
-
-
-
-
-
-
-
-
- {{ ::'importWorkAsBackground' | translate }}
-
-
-
-
-
-
-
-
-
-
-
- {{ ::'PROMPT' | translate }}
-
-
-
-
- {{ ::'BACKGROUND_IMAGE' | translate }}
-
-
-
- insert_photo
-
- {{ ::'chooseAnImage' | translate }}
-
-
-
-
-
- {{ ::'label.canvasWidth' | translate }}
-
-
-
- {{ ::'label.canvasHeight' | translate }}
-
-
-
-
-
- {{ ::'label.pointSize' | translate }}
-
-
-
- {{ ::'label.fontSize' | translate }}
-
-
-
- {{ ::'label.labelWidth' | translate }}
-
-
-
-
-
- {{ ::'label.canStudentCreateLabels' | translate }}
-
-
-
- {{ ::'label.enableDots' | translate }}
-
-
-
- {{ ::'label.allowStudentToUploadImageForBackground' | translate }}
-
-
-
-
-
{{ ::'label.starterLabels' | translate }}
-
- add
-
- {{ ::'label.addStarterLabel' | translate }}
-
-
-
-
- {{ ::'label.thereAreNoStarterLabels' | translate }}
-
-
-
-
- create
-
- {{ ::'label.saveStarterLabels' | translate }}
-
-
-
- delete_sweep
-
- {{ ::'label.deleteStarterLabels' | translate }}
-
-
-
-
-
diff --git a/src/main/webapp/wise5/components/label/label-authoring/label-authoring.component.html b/src/main/webapp/wise5/components/label/label-authoring/label-authoring.component.html
new file mode 100644
index 0000000000..a01d9419f0
--- /dev/null
+++ b/src/main/webapp/wise5/components/label/label-authoring/label-authoring.component.html
@@ -0,0 +1,236 @@
+
+ Prompt
+
+
+
+
+ Background Image
+
+
+
+ insert_photo
+
+
+
+
+ Canvas Width (px)
+
+
+
+ Canvas Height (px)
+
+
+
+
+
+ Point Radius Size (px)
+
+
+
+ Font Size
+
+
+
+ Label Max Character Width
+
+
+
+
+
+
+ Can Student Create Labels
+
+
+
+
+ Enable Dots
+
+
+
+
+ Allow Student to Upload Image for Background
+
+
+
+
+
+ Starter Labels
+
+ add
+
+
+
+ There are no starter labels. Click the "Add Label" button to add a starter label.
+
+
+
+
+
+ create
+
+
+ delete_sweep
+
+
\ No newline at end of file
diff --git a/src/main/webapp/wise5/components/label/label-authoring/label-authoring.component.scss b/src/main/webapp/wise5/components/label/label-authoring/label-authoring.component.scss
new file mode 100644
index 0000000000..2115fcb9d5
--- /dev/null
+++ b/src/main/webapp/wise5/components/label/label-authoring/label-authoring.component.scss
@@ -0,0 +1,64 @@
+.prompt {
+ width: 100%;
+}
+
+.background-image {
+ width: 80%;
+}
+
+.input {
+ margin-right: 20px;
+}
+
+.checkbox {
+ margin-top: 10px;
+ margin-bottom: 10px;
+}
+
+.starter-labels-button-container {
+ margin-top: 20px;
+ margin-bottom: 20px;
+}
+
+.starter-labels-button-label {
+ margin-right: 10px;
+}
+
+.starter-label-container {
+ border: 2px solid #dddddd;
+ border-radius: 5px;
+ margin-bottom: 10px;
+ padding: 20px 20px 10px 20px;
+}
+
+.info-block {
+ margin-bottom: 20px;
+ text-align: center;
+ font-weight: 500;
+}
+
+.label-input {
+ width: 40%;
+}
+
+.color-input {
+ width: 30%;
+}
+
+.coordinate-location-label {
+ margin-right: 20px;
+}
+
+.coordinate-input {
+ width: 15%;
+}
+
+.starter-labels-buttons-container {
+ margin-top: 20px;
+ margin-bottom: 10px;
+ margin-left: 10px;
+}
+
+.starter-labels-button {
+ margin-right: 10px;
+}
\ No newline at end of file
diff --git a/src/main/webapp/wise5/components/label/label-authoring/label-authoring.component.ts b/src/main/webapp/wise5/components/label/label-authoring/label-authoring.component.ts
new file mode 100644
index 0000000000..f37ce78551
--- /dev/null
+++ b/src/main/webapp/wise5/components/label/label-authoring/label-authoring.component.ts
@@ -0,0 +1,134 @@
+'use strict';
+
+import { Component } from '@angular/core';
+import { ComponentAuthoring } from '../../../authoringTool/components/component-authoring.component';
+import { ConfigService } from '../../../services/configService';
+import { NodeService } from '../../../services/nodeService';
+import { ProjectAssetService } from '../../../../site/src/app/services/projectAssetService';
+import { TeacherProjectService } from '../../../services/teacherProjectService';
+import { Subject, Subscription } from 'rxjs';
+import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
+
+@Component({
+ selector: 'label-authoring',
+ templateUrl: 'label-authoring.component.html',
+ styleUrls: ['label-authoring.component.scss']
+})
+export class LabelAuthoring extends ComponentAuthoring {
+ numberInputChange: Subject = new Subject();
+ textInputChange: Subject = new Subject();
+
+ numberInputChangeSubscription: Subscription;
+ textInputChangeSubscription: Subscription;
+
+ constructor(
+ protected ConfigService: ConfigService,
+ protected NodeService: NodeService,
+ protected ProjectAssetService: ProjectAssetService,
+ protected ProjectService: TeacherProjectService
+ ) {
+ super(ConfigService, NodeService, ProjectAssetService, ProjectService);
+ this.numberInputChangeSubscription = this.numberInputChange
+ .pipe(debounceTime(1000), distinctUntilChanged())
+ .subscribe(() => {
+ this.componentChanged();
+ });
+ this.textInputChangeSubscription = this.textInputChange
+ .pipe(debounceTime(1000), distinctUntilChanged())
+ .subscribe(() => {
+ this.componentChanged();
+ });
+ }
+
+ ngOnInit() {
+ super.ngOnInit();
+ if (this.authoringComponentContent.enableCircles == null) {
+ // If this component was created before enableCircles was implemented, we will default it to
+ // true in the authoring so that the "Enable Dots" checkbox is checked.
+ this.authoringComponentContent.enableCircles = true;
+ }
+ }
+
+ ngOnDestroy() {
+ super.ngOnDestroy();
+ this.unsubscribeAll();
+ }
+
+ unsubscribeAll() {
+ this.numberInputChangeSubscription.unsubscribe();
+ this.textInputChangeSubscription.unsubscribe();
+ }
+
+ addLabelClicked(): void {
+ const newLabel = {
+ text: $localize`Enter text here`,
+ color: 'blue',
+ pointX: 100,
+ pointY: 100,
+ textX: 200,
+ textY: 200,
+ canEdit: false,
+ canDelete: false
+ };
+ this.authoringComponentContent.labels.push(newLabel);
+ this.componentChanged();
+ }
+
+ deleteLabelClicked(index: number, label: any): void {
+ if (confirm($localize`Are you sure you want to delete this label?\n\n${label.textString}`)) {
+ this.authoringComponentContent.labels.splice(index, 1);
+ this.componentChanged();
+ }
+ }
+
+ chooseBackgroundImage(): void {
+ const params = {
+ isPopup: true,
+ nodeId: this.nodeId,
+ componentId: this.componentId,
+ target: 'background'
+ };
+ this.openAssetChooser(params);
+ }
+
+ assetSelected({ nodeId, componentId, assetItem, target }): void {
+ super.assetSelected({ nodeId, componentId, assetItem, target });
+ const fileName = assetItem.fileName;
+ if (target === 'background') {
+ this.authoringComponentContent.backgroundImage = fileName;
+ this.componentChanged();
+ }
+ }
+
+ saveStarterLabels(): void {
+ if (confirm($localize`Are you sure you want to save the starter labels?`)) {
+ this.NodeService.requestStarterState({ nodeId: this.nodeId, componentId: this.componentId });
+ }
+ }
+
+ saveStarterState(starterState: any): void {
+ this.authoringComponentContent.labels = starterState;
+ this.componentChanged();
+ }
+
+ compareTextAlphabetically(stringA: string, stringB: string) {
+ if (stringA < stringB) {
+ return -1;
+ } else if (stringA > stringB) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ deleteStarterLabels(): void {
+ if (confirm($localize`label.areYouSureYouWantToDeleteAllTheStarterLabels`)) {
+ this.authoringComponentContent.labels = [];
+ this.componentChanged();
+ }
+ }
+
+ openColorViewer(): void {
+ window.open('http://www.javascripter.net/faq/colornam.htm');
+ }
+}
diff --git a/src/main/webapp/wise5/components/label/labelAuthoring.ts b/src/main/webapp/wise5/components/label/labelAuthoring.ts
deleted file mode 100644
index df29a8ac22..0000000000
--- a/src/main/webapp/wise5/components/label/labelAuthoring.ts
+++ /dev/null
@@ -1,177 +0,0 @@
-'use strict';
-
-import * as $ from 'jquery';
-import * as fabric from 'fabric';
-window['fabric'] = fabric.fabric;
-import html2canvas from 'html2canvas';
-import { Directive } from '@angular/core';
-import { EditComponentController } from '../../authoringTool/components/editComponentController';
-
-@Directive()
-class LabelAuthoringController extends EditComponentController {
- static $inject = [
- '$filter',
- '$window',
- 'ConfigService',
- 'NodeService',
- 'NotificationService',
- 'ProjectAssetService',
- 'ProjectService',
- 'UtilService'
- ];
-
- constructor(
- $filter,
- private $window,
- ConfigService,
- NodeService,
- NotificationService,
- ProjectAssetService,
- ProjectService,
- UtilService
- ) {
- super(
- $filter,
- ConfigService,
- NodeService,
- NotificationService,
- ProjectAssetService,
- ProjectService,
- UtilService
- );
- }
-
- $onInit() {
- super.$onInit();
- if (this.authoringComponentContent.enableCircles == null) {
- /*
- * If this component was created before enableCircles was implemented,
- * we will default it to true in the authoring so that the
- * "Enable Dots" checkbox is checked.
- */
- this.authoringComponentContent.enableCircles = true;
- }
- }
-
- addLabelClicked(): void {
- const newLabel = {
- text: this.$translate('label.enterTextHere'),
- color: 'blue',
- pointX: 100,
- pointY: 100,
- textX: 200,
- textY: 200,
- canEdit: false,
- canDelete: false
- };
- this.authoringComponentContent.labels.push(newLabel);
- this.componentChanged();
- }
-
- /**
- * Delete a label in the authoring view
- * @param index the index of the label in the labels array
- */
- deleteLabelClicked(index: number, label: any): void {
- const answer = confirm(
- this.$translate('label.areYouSureYouWantToDeleteThisLabel', {
- selectedLabelText: label.textString
- })
- );
- if (answer) {
- this.authoringComponentContent.labels.splice(index, 1);
- this.componentChanged();
- }
- }
-
- chooseBackgroundImage(): void {
- const params = {
- isPopup: true,
- nodeId: this.nodeId,
- componentId: this.componentId,
- target: 'background'
- };
- this.openAssetChooser(params);
- }
-
- assetSelected({ nodeId, componentId, assetItem, target }): void {
- super.assetSelected({ nodeId, componentId, assetItem, target });
- const fileName = assetItem.fileName;
- if (target === 'background') {
- this.authoringComponentContent.backgroundImage = fileName;
- this.componentChanged();
- }
- }
-
- saveStarterLabels(): void {
- if (confirm(this.$translate('label.areYouSureYouWantToSaveTheStarterLabels'))) {
- this.NodeService.requestStarterState({ nodeId: this.nodeId, componentId: this.componentId });
- }
- }
-
- saveStarterState(starterState: any): void {
- starterState.sort(this.labelTextComparator);
- this.authoringComponentContent.labels = starterState;
- this.componentChanged();
- }
-
- /**
- * A comparator used to sort labels alphabetically
- * It should be used like labels.sort(this.labelTextComparator);
- * @param labelA a label object
- * @param labelB a label object
- * @return -1 if labelA comes before labelB
- * 1 if labelB comes after labelB
- * 0 of the labels are equal
- */
- labelTextComparator(labelA: any, labelB: any): number {
- if (labelA.text < labelB.text) {
- return -1;
- } else if (labelA.text > labelB.text) {
- return 1;
- } else {
- if (labelA.color < labelB.color) {
- return -1;
- } else if (labelA.color > labelB.color) {
- return 1;
- } else {
- if (labelA.pointX < labelB.pointX) {
- return -1;
- } else if (labelA.pointX > labelB.pointX) {
- return 1;
- } else {
- if (labelA.pointY < labelB.pointY) {
- return -1;
- } else if (labelA.pointY > labelB.pointY) {
- return 1;
- } else {
- return 0;
- }
- }
- }
- }
- }
-
- deleteStarterLabels(): void {
- if (confirm(this.$translate('label.areYouSureYouWantToDeleteAllTheStarterLabels'))) {
- this.authoringComponentContent.labels = [];
- this.componentChanged();
- }
- }
-
- openColorViewer(): void {
- this.$window.open('http://www.javascripter.net/faq/colornam.htm');
- }
-}
-
-const LabelAuthoring = {
- bindings: {
- nodeId: '@',
- componentId: '@'
- },
- controller: LabelAuthoringController,
- controllerAs: 'labelController',
- templateUrl: 'wise5/components/label/authoring.html'
-};
-
-export default LabelAuthoring;
diff --git a/src/main/webapp/wise5/components/label/labelAuthoringComponentModule.ts b/src/main/webapp/wise5/components/label/labelAuthoringComponentModule.ts
index cceefafef9..e6bd14a03c 100644
--- a/src/main/webapp/wise5/components/label/labelAuthoringComponentModule.ts
+++ b/src/main/webapp/wise5/components/label/labelAuthoringComponentModule.ts
@@ -1,15 +1,18 @@
'use strict';
import * as angular from 'angular';
-import { downgradeInjectable } from '@angular/upgrade/static';
+import { downgradeComponent, downgradeInjectable } from '@angular/upgrade/static';
import { LabelService } from './labelService';
-import LabelAuthoring from './labelAuthoring';
import { EditLabelAdvancedComponent } from './edit-label-advanced/edit-label-advanced.component';
+import { LabelAuthoring } from './label-authoring/label-authoring.component';
const labelAuthoringComponentModule = angular
.module('labelAuthoringComponentModule', ['pascalprecht.translate'])
.service('LabelService', downgradeInjectable(LabelService))
- .component('labelAuthoring', LabelAuthoring)
+ .directive(
+ 'labelAuthoring',
+ downgradeComponent({ component: LabelAuthoring }) as angular.IDirectiveFactory
+ )
.component('editLabelAdvanced', EditLabelAdvancedComponent)
.config([
'$translatePartialLoaderProvider',
From 6c76e3f59d8263895e122c043d16d93feba56b24 Mon Sep 17 00:00:00 2001
From: Geoffrey Kwan
Date: Mon, 25 Jan 2021 14:34:21 -0500
Subject: [PATCH 03/13] Ran i18n translation generation for Label authoring.
#2895
---
src/main/webapp/site/src/messages.xlf | 222 ++++++++++++++++++++++++++
1 file changed, 222 insertions(+)
diff --git a/src/main/webapp/site/src/messages.xlf b/src/main/webapp/site/src/messages.xlf
index 2419779b02..c7e6ac4b89 100644
--- a/src/main/webapp/site/src/messages.xlf
+++ b/src/main/webapp/site/src/messages.xlf
@@ -6024,6 +6024,10 @@
../../wise5/components/draw/draw-authoring/draw-authoring.component.html
2
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 2
+
../../wise5/components/match/match-authoring/match-authoring.component.html
2
@@ -6047,6 +6051,10 @@
../../wise5/components/draw/draw-authoring/draw-authoring.component.html
6
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 6
+
../../wise5/components/match/match-authoring/match-authoring.component.html
6
@@ -6105,6 +6113,10 @@
../../wise5/components/draw/draw-authoring/draw-authoring.component.html
228
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 21
+
../../wise5/components/match/match-authoring/match-authoring.component.html
41
@@ -6189,6 +6201,10 @@
../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html
87
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 24
+
../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html
68
@@ -6316,6 +6332,14 @@
app/authoring-tool/edit-component-tags/edit-component-tags.component.html
38
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 206
+
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 232
+
../../wise5/components/match/match-authoring/match-authoring.component.html
79
@@ -6378,6 +6402,10 @@
../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html
195
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 128
+
(Optional) Create a starting state for the concept map by editing the "Student Preview" below and then saving here:
@@ -6613,6 +6641,10 @@
../../wise5/components/draw/draw-authoring/draw-authoring.component.html
277
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 221
+
Delete Starter Drawing
@@ -6691,6 +6723,196 @@
2
+
+ Background Image
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 13
+
+
+
+ Canvas Width (px)
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 31
+
+
+
+ Canvas Height (px)
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 38
+
+
+
+ Point Radius Size (px)
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 47
+
+
+
+ Font Size
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 54
+
+
+
+ Label Max Character Width
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 61
+
+
+
+ Can Student Create Labels
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 74
+
+
+
+ Enable Dots
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 83
+
+
+
+ Allow Student to Upload Image for Background
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 92
+
+
+
+ Starter Labels
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 99
+
+
+
+ Add Starter Label
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 103
+
+
+
+ add
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 106
+
+
+
+ There are no starter labels. Click the "Add Label" button to add a starter label.
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 113
+
+
+
+ Text
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 120
+
+
+
+ Enter Label Text Here
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 124
+
+
+
+ View Colors
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 137
+
+
+
+ Color Palette
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 140
+
+
+
+ Can Student Edit Label
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 151
+
+
+
+ Can Student Delete Label
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 160
+
+
+
+ Point Location
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 167
+
+
+
+ X
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 169
+
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 186
+
+
+
+ Y
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 176
+
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 193
+
+
+
+ Text Location
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 184
+
+
+
+ Delete Label
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 203
+
+
+
+ Save Starter Labels
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 218
+
+
+
+ Delete Starter Labels
+
+ ../../wise5/components/label/label-authoring/label-authoring.component.html
+ 229
+
+
Choices
From 77c6ad411f269fa3be5fda11439b6f1b5dd6d7c2 Mon Sep 17 00:00:00 2001
From: Hiroki Terashima
Date: Mon, 25 Jan 2021 11:50:01 -0800
Subject: [PATCH 04/13] 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 b8d87ca506e8e06cd6886b72968fba58ca8df93d Mon Sep 17 00:00:00 2001
From: Hiroki Terashima
Date: Mon, 25 Jan 2021 12:22:16 -0800
Subject: [PATCH 05/13] Extracted chooseBackgroundImage to parent. Fixed typo
label.textString -> label.text. #2895
---
.../components/component-authoring.component.ts | 10 ++++++++++
.../concept-map-authoring.component.ts | 10 ----------
.../draw-authoring/draw-authoring.component.ts | 10 ----------
.../label-authoring.component.html | 6 +++---
.../label-authoring/label-authoring.component.ts | 16 +++-------------
5 files changed, 16 insertions(+), 36 deletions(-)
diff --git a/src/main/webapp/wise5/authoringTool/components/component-authoring.component.ts b/src/main/webapp/wise5/authoringTool/components/component-authoring.component.ts
index 225b94249e..67ae4403ea 100644
--- a/src/main/webapp/wise5/authoringTool/components/component-authoring.component.ts
+++ b/src/main/webapp/wise5/authoringTool/components/component-authoring.component.ts
@@ -108,6 +108,16 @@ export abstract class ComponentAuthoring {
});
}
+ chooseBackgroundImage(): void {
+ const params = {
+ isPopup: true,
+ nodeId: this.nodeId,
+ componentId: this.componentId,
+ target: 'background'
+ };
+ this.openAssetChooser(params);
+ }
+
openAssetChooser(params: any): any {
return this.ProjectAssetService.openAssetChooser(params).then((data: any) => {
return this.assetSelected(data);
diff --git a/src/main/webapp/wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.ts b/src/main/webapp/wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.ts
index 5a140470a4..9fbf33365e 100644
--- a/src/main/webapp/wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.ts
+++ b/src/main/webapp/wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.ts
@@ -156,16 +156,6 @@ export class ConceptMapAuthoring extends ComponentAuthoring {
}
}
- chooseBackgroundImage(): void {
- const params = {
- isPopup: true,
- nodeId: this.nodeId,
- componentId: this.componentId,
- target: 'background'
- };
- this.openAssetChooser(params);
- }
-
chooseNodeImage(conceptMapNodeId: string): void {
const params = {
isPopup: true,
diff --git a/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.ts b/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.ts
index 930150944a..c17659ba9a 100644
--- a/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.ts
+++ b/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.ts
@@ -176,16 +176,6 @@ export class DrawAuthoring extends ComponentAuthoring {
this.componentChanged();
}
- chooseBackgroundImage(): void {
- const params = {
- isPopup: true,
- nodeId: this.nodeId,
- componentId: this.componentId,
- target: 'background'
- };
- this.openAssetChooser(params);
- }
-
chooseStampImage(stampIndex: number): void {
const params = {
isPopup: true,
diff --git a/src/main/webapp/wise5/components/label/label-authoring/label-authoring.component.html b/src/main/webapp/wise5/components/label/label-authoring/label-authoring.component.html
index a01d9419f0..9aa57edb6b 100644
--- a/src/main/webapp/wise5/components/label/label-authoring/label-authoring.component.html
+++ b/src/main/webapp/wise5/components/label/label-authoring/label-authoring.component.html
@@ -99,7 +99,7 @@
Starter Labels
delete_sweep
-
\ No newline at end of file
+
diff --git a/src/main/webapp/wise5/components/label/label-authoring/label-authoring.component.ts b/src/main/webapp/wise5/components/label/label-authoring/label-authoring.component.ts
index f37ce78551..2c2ea4ff2b 100644
--- a/src/main/webapp/wise5/components/label/label-authoring/label-authoring.component.ts
+++ b/src/main/webapp/wise5/components/label/label-authoring/label-authoring.component.ts
@@ -59,7 +59,7 @@ export class LabelAuthoring extends ComponentAuthoring {
this.textInputChangeSubscription.unsubscribe();
}
- addLabelClicked(): void {
+ addLabel(): void {
const newLabel = {
text: $localize`Enter text here`,
color: 'blue',
@@ -74,23 +74,13 @@ export class LabelAuthoring extends ComponentAuthoring {
this.componentChanged();
}
- deleteLabelClicked(index: number, label: any): void {
- if (confirm($localize`Are you sure you want to delete this label?\n\n${label.textString}`)) {
+ deleteLabel(index: number, label: any): void {
+ if (confirm($localize`Are you sure you want to delete this label?\n\n${label.text}`)) {
this.authoringComponentContent.labels.splice(index, 1);
this.componentChanged();
}
}
- chooseBackgroundImage(): void {
- const params = {
- isPopup: true,
- nodeId: this.nodeId,
- componentId: this.componentId,
- target: 'background'
- };
- this.openAssetChooser(params);
- }
-
assetSelected({ nodeId, componentId, assetItem, target }): void {
super.assetSelected({ nodeId, componentId, assetItem, target });
const fileName = assetItem.fileName;
From f592801f5960bd30e9f7fe9513d2382c2d65d510 Mon Sep 17 00:00:00 2001
From: Hiroki Terashima
Date: Mon, 25 Jan 2021 15:10:21 -0800
Subject: [PATCH 06/13] Removed duplication in milestoneservice tests. #2897
---
.../src/app/services/milestoneService.spec.ts | 214 +++---------------
1 file changed, 26 insertions(+), 188 deletions(-)
diff --git a/src/main/webapp/site/src/app/services/milestoneService.spec.ts b/src/main/webapp/site/src/app/services/milestoneService.spec.ts
index a9d7ec91b9..69177122c3 100644
--- a/src/main/webapp/site/src/app/services/milestoneService.spec.ts
+++ b/src/main/webapp/site/src/app/services/milestoneService.spec.ts
@@ -48,6 +48,10 @@ const aggregateAutoScoresSample = {
const possibleScoresKi = [1, 2, 3, 4, 5];
+const sampleAggregateData = {
+ counts: createScoreCounts([10, 20, 30, 40, 50])
+};
+
const reportSettingsCustomScoreValuesSample = {
customScoreValues: {
ki: [1, 2, 3, 4]
@@ -964,205 +968,39 @@ function isPercentOfScoresNotEqualTo() {
function getComparatorSum() {
describe('getComparatorSum()', () => {
- getGreaterThanSum();
- getGreaterThanOrEqualToSum();
- getLessThanSum();
- getEqualToSum();
- getNotEqualToSum();
- });
-}
-
-function getGreaterThanSum() {
- const aggregateData = {
- counts: createScoreCounts([10, 20, 30, 40, 50])
- };
- it('should get greater than sum with score 1', () => {
- const satisfyCriterion = { value: 1 };
- expect(
- service.getComparatorSum(
- satisfyCriterion,
- aggregateData,
- possibleScoresKi,
- utilService.greaterThan
- )
- ).toEqual(140);
- });
- it('should get greater than sum with score 2', () => {
- const satisfyCriterion = { value: 2 };
- expect(
- service.getComparatorSum(
- satisfyCriterion,
- aggregateData,
- possibleScoresKi,
- utilService.greaterThan
- )
- ).toEqual(120);
- });
- it('should get greater than sum with score 3', () => {
- const satisfyCriterion = { value: 3 };
- expect(
- service.getComparatorSum(
- satisfyCriterion,
- aggregateData,
- possibleScoresKi,
- utilService.greaterThan
- )
- ).toEqual(90);
- });
- it('should get greater than sum with score 4', () => {
- const satisfyCriterion = { value: 4 };
- expect(
- service.getComparatorSum(
- satisfyCriterion,
- aggregateData,
- possibleScoresKi,
- utilService.greaterThan
- )
- ).toEqual(50);
+ getComparatorSum_greaterThan0_ReturnSumAll();
+ getComparatorSum_greaterThan3_ReturnSumPartial();
+ getComparatorSum_greaterThan5_Return0();
});
}
-function getGreaterThanOrEqualToSum() {
- const aggregateData = {
- counts: createScoreCounts([10, 20, 30, 40, 50])
- };
- it('should get greater than or equal to sum with score 1', () => {
- const satisfyCriterion = { value: 1 };
- expect(
- service.getComparatorSum(
- satisfyCriterion,
- aggregateData,
- possibleScoresKi,
- utilService.greaterThanEqualTo
- )
- ).toEqual(150);
- });
- it('should get greater than or equal to sum with score 2', () => {
- const satisfyCriterion = { value: 2 };
- expect(
- service.getComparatorSum(
- satisfyCriterion,
- aggregateData,
- possibleScoresKi,
- utilService.greaterThanEqualTo
- )
- ).toEqual(140);
- });
- it('should get greater than or equal to sum with score 3', () => {
- const satisfyCriterion = { value: 3 };
- expect(
- service.getComparatorSum(
- satisfyCriterion,
- aggregateData,
- possibleScoresKi,
- utilService.greaterThanEqualTo
- )
- ).toEqual(120);
- });
- it('should get greater than or equal to sum with score 4', () => {
- const satisfyCriterion = { value: 4 };
- expect(
- service.getComparatorSum(
- satisfyCriterion,
- aggregateData,
- possibleScoresKi,
- utilService.greaterThanEqualTo
- )
- ).toEqual(90);
- });
- it('should get greater than or equal to sum with score 5', () => {
- const satisfyCriterion = { value: 5 };
- expect(
- service.getComparatorSum(
- satisfyCriterion,
- aggregateData,
- possibleScoresKi,
- utilService.greaterThanEqualTo
- )
- ).toEqual(50);
- });
+function expectComparatorResult(satisfyCriterionValue: number, expectedResult: number) {
+ const satisfyCriterion = { value: satisfyCriterionValue };
+ expect(
+ service.getComparatorSum(
+ satisfyCriterion,
+ sampleAggregateData,
+ possibleScoresKi,
+ utilService.greaterThan
+ )
+ ).toEqual(expectedResult);
}
-function getLessThanSum() {
- const aggregateData = {
- counts: createScoreCounts([10, 20, 30, 40, 50])
- };
- it('should get less than sum with score 2', () => {
- const satisfyCriterion = { value: 2 };
- expect(
- service.getComparatorSum(
- satisfyCriterion,
- aggregateData,
- possibleScoresKi,
- utilService.lessThan
- )
- ).toEqual(10);
- });
- it('should get less than sum with score 3', () => {
- const satisfyCriterion = { value: 3 };
- expect(
- service.getComparatorSum(
- satisfyCriterion,
- aggregateData,
- possibleScoresKi,
- utilService.lessThan
- )
- ).toEqual(30);
- });
- it('should get less than sum with score 4', () => {
- const satisfyCriterion = { value: 4 };
- expect(
- service.getComparatorSum(
- satisfyCriterion,
- aggregateData,
- possibleScoresKi,
- utilService.lessThan
- )
- ).toEqual(60);
- });
- it('should get less than sum with score 5', () => {
- const satisfyCriterion = { value: 5 };
- expect(
- service.getComparatorSum(
- satisfyCriterion,
- aggregateData,
- possibleScoresKi,
- utilService.lessThan
- )
- ).toEqual(100);
+function getComparatorSum_greaterThan0_ReturnSumAll() {
+ it('should get greater than sum with score 0', () => {
+ expectComparatorResult(0, 150);
});
}
-function getEqualToSum() {
- it('should return the sum of scores equal to value', () => {
- const satisfyCriterion = { value: 3 };
- const aggregateData = {
- counts: createScoreCounts([10, 20, 30, 40, 50])
- };
- expect(
- service.getComparatorSum(
- satisfyCriterion,
- aggregateData,
- possibleScoresKi,
- utilService.equalTo
- )
- ).toEqual(30);
+function getComparatorSum_greaterThan3_ReturnSumPartial() {
+ it('should get greater than sum with score 3', () => {
+ expectComparatorResult(3, 90);
});
}
-function getNotEqualToSum() {
- const aggregateData = {
- counts: { 1: 2, 2: 0, 3: 1, 4: 0, 5: 0 },
- scoreCount: 3
- };
- it('should return the sum of scores not equal to value', () => {
- const result = service.getComparatorSum(
- satisfyCriterionSample,
- aggregateData,
- possibleScoresKi,
- utilService.notEqualTo
- );
- expect(result).toBe(2);
+function getComparatorSum_greaterThan5_Return0() {
+ it('should get greater than sum with score 5', () => {
+ expectComparatorResult(5, 0);
});
}
From 6f28ba9bf0942ee017e3e39588c3828b12e66714 Mon Sep 17 00:00:00 2001
From: Hiroki Terashima
Date: Tue, 26 Jan 2021 09:24:00 -0800
Subject: [PATCH 07/13] 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 0d46bcdd44a9e020ace15285e0c7890ba856f925 Mon Sep 17 00:00:00 2001
From: Geoffrey Kwan
Date: Wed, 27 Jan 2021 15:06:26 -0500
Subject: [PATCH 08/13] Upgraded Table authoring to Angular. #2902
---
.../src/app/teacher-hybrid-angular.module.ts | 2 +
src/main/webapp/site/src/messages.xlf | 123 +++++
.../wise5/components/table/authoring.html | 381 ----------------
.../table-authoring.component.html | 177 ++++++++
.../table-authoring.component.scss | 62 +++
.../table-authoring.component.ts | 364 +++++++++++++++
.../wise5/components/table/tableAuthoring.ts | 428 ------------------
.../table/tableAuthoringComponentModule.ts | 9 +-
8 files changed, 734 insertions(+), 812 deletions(-)
delete mode 100644 src/main/webapp/wise5/components/table/authoring.html
create mode 100644 src/main/webapp/wise5/components/table/table-authoring/table-authoring.component.html
create mode 100644 src/main/webapp/wise5/components/table/table-authoring/table-authoring.component.scss
create mode 100644 src/main/webapp/wise5/components/table/table-authoring/table-authoring.component.ts
delete mode 100644 src/main/webapp/wise5/components/table/tableAuthoring.ts
diff --git a/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts b/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts
index 6a5134318d..a7e51fad62 100644
--- a/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts
+++ b/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts
@@ -52,6 +52,7 @@ import { ConceptMapAuthoring } from '../../../wise5/components/conceptMap/concep
import { DrawAuthoring } from '../../../wise5/components/draw/draw-authoring/draw-authoring.component';
import { MatchAuthoring } from '../../../wise5/components/match/match-authoring/match-authoring.component';
import { LabelAuthoring } from '../../../wise5/components/label/label-authoring/label-authoring.component';
+import { TableAuthoring } from '../../../wise5/components/table/table-authoring/table-authoring.component';
@NgModule({
declarations: [
@@ -87,6 +88,7 @@ import { LabelAuthoring } from '../../../wise5/components/label/label-authoring/
RubricAuthoringComponent,
StatusIconComponent,
StepInfoComponent,
+ TableAuthoring,
WorkgroupInfoComponent,
WorkgroupNodeScoreComponent,
WorkgroupSelectAutocompleteComponent,
diff --git a/src/main/webapp/site/src/messages.xlf b/src/main/webapp/site/src/messages.xlf
index c7e6ac4b89..f268ec041c 100644
--- a/src/main/webapp/site/src/messages.xlf
+++ b/src/main/webapp/site/src/messages.xlf
@@ -6040,6 +6040,10 @@
../../wise5/components/openResponse/open-response-authoring/open-response-authoring.component.html
11
+
+ ../../wise5/components/table/table-authoring/table-authoring.component.html
+ 2
+
Enter Prompt Here
@@ -6067,6 +6071,10 @@
../../wise5/components/openResponse/open-response-authoring/open-response-authoring.component.html
15
+
+ ../../wise5/components/table/table-authoring/table-authoring.component.html
+ 6
+
Background Image (Optional)
@@ -6356,6 +6364,14 @@
../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html
124
+
+ ../../wise5/components/table/table-authoring/table-authoring.component.html
+ 58
+
+
+ ../../wise5/components/table/table-authoring/table-authoring.component.html
+ 99
+
Show Node Labels
@@ -7289,6 +7305,113 @@
6
+
+ Columns
+
+ ../../wise5/components/table/table-authoring/table-authoring.component.html
+ 13
+
+
+
+ Rows
+
+ ../../wise5/components/table/table-authoring/table-authoring.component.html
+ 20
+
+
+
+ Global Cell Size
+
+ ../../wise5/components/table/table-authoring/table-authoring.component.html
+ 27
+
+
+
+ Insert Column Before
+
+ ../../wise5/components/table/table-authoring/table-authoring.component.html
+ 44
+
+
+ ../../wise5/components/table/table-authoring/table-authoring.component.html
+ 47
+
+
+
+ Delete Column
+
+ ../../wise5/components/table/table-authoring/table-authoring.component.html
+ 55
+
+
+
+ Insert Column After
+
+ ../../wise5/components/table/table-authoring/table-authoring.component.html
+ 67
+
+
+ ../../wise5/components/table/table-authoring/table-authoring.component.html
+ 70
+
+
+
+ Insert Row Before
+
+ ../../wise5/components/table/table-authoring/table-authoring.component.html
+ 85
+
+
+ ../../wise5/components/table/table-authoring/table-authoring.component.html
+ 88
+
+
+
+ Delete Row
+
+ ../../wise5/components/table/table-authoring/table-authoring.component.html
+ 96
+
+
+
+ Insert Row After
+
+ ../../wise5/components/table/table-authoring/table-authoring.component.html
+ 108
+
+
+ ../../wise5/components/table/table-authoring/table-authoring.component.html
+ 111
+
+
+
+ Editable
+
+ ../../wise5/components/table/table-authoring/table-authoring.component.html
+ 130
+
+
+
+ Column Cell Size
+
+ ../../wise5/components/table/table-authoring/table-authoring.component.html
+ 152
+
+
+
+ Make All Cells Editable
+
+ ../../wise5/components/table/table-authoring/table-authoring.component.html
+ 167
+
+
+
+ Make All Cells Uneditable
+
+ ../../wise5/components/table/table-authoring/table-authoring.component.html
+ 174
+
+
(Team )
diff --git a/src/main/webapp/wise5/components/table/authoring.html b/src/main/webapp/wise5/components/table/authoring.html
deleted file mode 100644
index 08bbdb80d3..0000000000
--- a/src/main/webapp/wise5/components/table/authoring.html
+++ /dev/null
@@ -1,381 +0,0 @@
-
-
-
-
-
-
-
- {{ ::'SHOW_SAVE_BUTTON' | translate }}
-
-
-
-
- {{ ::'SHOW_SUBMIT_BUTTON' | translate }}
-
-
-
-
- {{ ::'SHOW_ADD_TO_NOTEBOOK_BUTTON' | translate }}
-
-
-
-
- {{ ::'MAX_SUBMIT' | translate }}
-
-
-
-
-
{{ ::'table.dataExplorer' | translate }}
-
-
- {{ ::'table.enableDataExplorer' | translate }}
-
-
-
-
-
{{ ::'table.allowedGraphTypes' | translate }}
-
- {{ ::'table.scatterPlot' | translate }}
-
-
- {{ ::'table.lineGraph' | translate }}
-
-
- {{ ::'table.barGraph' | translate }}
-
-
-
-
- {{ ::'table.showScatterPlotRegressionLine' | translate }}
-
-
-
-
- {{ ::'table.numberOfSeries' | translate }}
-
-
-
-
-
- {{ ::'table.numberOfYAxes' | translate }}
-
-
-
-
-
-
- {{ ::'table.series' | translate }} {{ $index + 1 }}
-
-
-
- {{ ::'table.yAxis' | translate }} {{ $index + 1 }}
-
-
-
-
-
-
-
-
- {{ ::'table.canStudentEditAxisLabels' | translate }}
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ ::'CONNECTED_COMPONENTS' | translate }}
-
-
- add
-
- {{ ::'ADD_CONNECTED_COMPONENT' | translate }}
-
-
-
-
-
-
- {{ ::'step' | translate }}
-
-
- {{tableController.getNodePositionAndTitleByNodeId(item.$key)}}
-
-
-
-
- {{ ::'component' | translate }}
-
-
- {{ componentIndex + 1 }}. {{component.type}}
-
- ({{ ::'thisComponent' | translate }})
-
-
-
-
-
- {{ ::'type' | translate }}
-
-
- {{ ::'importWork' | translate }}
-
-
- {{ ::'showWork' | translate }}
-
-
-
-
- {{ ::'action' | translate }}
-
-
- {{ ::'merge' | translate }}
-
-
- {{ ::'append' | translate }}
-
-
-
-
-
-
- delete
-
- {{ ::'DELETE' | translate }}
-
-
-
-
-
- {{ ::'table.onlyShowDataAtMouseXPosition' | translate }}
-
-
-
-
-
-
-
-
-
-
- {{ ::'PROMPT' | translate }}
-
-
-
-
-
- {{ ::'table.columns' | translate }}
-
-
-
- {{ ::'table.rows' | translate }}
-
-
-
- {{ ::'table.globalCellSize' | translate }}
-
-
-
-
-
-
-
-
- {{ ::'table.makeAllCellsEditable' | translate }}
-
-
- {{ ::'table.makeAllCellsUneditable' | translate }}
-
-
-
-
-
diff --git a/src/main/webapp/wise5/components/table/table-authoring/table-authoring.component.html b/src/main/webapp/wise5/components/table/table-authoring/table-authoring.component.html
new file mode 100644
index 0000000000..5995f387dd
--- /dev/null
+++ b/src/main/webapp/wise5/components/table/table-authoring/table-authoring.component.html
@@ -0,0 +1,177 @@
+
+ Prompt
+
+
+
+
+ Columns
+
+
+
+ Rows
+
+
+
+ Global Cell Size
+
+
+
+
+
+
+
+
+
+ loupe
+
+
+ delete
+
+
+ loupe
+
+
+
+
+
+
+
+
+
+ loupe
+
+
+ delete
+
+
+ loupe
+
+
+
+
+
+
+
+
+ Editable
+
+
+
+
+
+
+
+
+
+
+
+
+ Optional
+
+
+
+
+
+ Column Cell Size
+
+
+
+
+
+
+
+
+ Make All Cells Editable
+
+
+ Make All Cells Uneditable
+
+
\ No newline at end of file
diff --git a/src/main/webapp/wise5/components/table/table-authoring/table-authoring.component.scss b/src/main/webapp/wise5/components/table/table-authoring/table-authoring.component.scss
new file mode 100644
index 0000000000..cdd97d67ff
--- /dev/null
+++ b/src/main/webapp/wise5/components/table/table-authoring/table-authoring.component.scss
@@ -0,0 +1,62 @@
+.prompt {
+ width: 100%;
+}
+
+.size-input {
+ width: 120px;
+ margin-right: 20px;
+}
+
+.table {
+ width: 100%;
+}
+
+.rotate90 {
+ -webkit-transform: rotate(90deg);
+ transform: rotate(90deg);
+}
+
+.rotate270 {
+ -webkit-transform: rotate(270deg);
+ transform: rotate(270deg);
+}
+
+.column-buttons {
+ margin: 5px;
+}
+
+.row-buttons {
+ margin-top: 5px;
+ margin-bottom: 5px;
+}
+
+.spacer {
+ width: 56px;
+}
+
+.outer-cell-container {
+ border: 1px solid black;
+ padding: 5px;
+ width: auto;
+}
+
+.inner-cell-container {
+ border: 1px solid black;
+ padding: 10px;
+}
+
+.cell-text {
+ width: 100%;
+}
+
+.blank-row-cell {
+ height: 20px;
+}
+
+::ng-deep .mat-form-field-infix {
+ width: auto !important;
+}
+
+.make-all-cells-editable-buttons {
+ margin: 10px;
+}
\ No newline at end of file
diff --git a/src/main/webapp/wise5/components/table/table-authoring/table-authoring.component.ts b/src/main/webapp/wise5/components/table/table-authoring/table-authoring.component.ts
new file mode 100644
index 0000000000..46e7b3c3c6
--- /dev/null
+++ b/src/main/webapp/wise5/components/table/table-authoring/table-authoring.component.ts
@@ -0,0 +1,364 @@
+'use strict';
+
+import { Component } from '@angular/core';
+import { Subject, Subscription } from 'rxjs';
+import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
+import { ProjectAssetService } from '../../../../site/src/app/services/projectAssetService';
+import { ComponentAuthoring } from '../../../authoringTool/components/component-authoring.component';
+import { ConfigService } from '../../../services/configService';
+import { NodeService } from '../../../services/nodeService';
+import { TeacherProjectService } from '../../../services/teacherProjectService';
+
+@Component({
+ selector: 'table-authoring',
+ templateUrl: 'table-authoring.component.html',
+ styleUrls: ['table-authoring.component.scss']
+})
+export class TableAuthoring extends ComponentAuthoring {
+ columnCellSizes: any;
+
+ numColumnsChange: Subject
= new Subject();
+ numRowsChange: Subject = new Subject();
+ globalCellSizeChange: Subject = new Subject();
+ inputChange: Subject = new Subject();
+
+ numColumnsChangeSubscription: Subscription;
+ numRowsChangeSubscription: Subscription;
+ globalCellSizeChangeSubscription: Subscription;
+ inputChangeSubscription: Subscription;
+
+ constructor(
+ protected ConfigService: ConfigService,
+ protected NodeService: NodeService,
+ protected ProjectAssetService: ProjectAssetService,
+ protected ProjectService: TeacherProjectService
+ ) {
+ super(ConfigService, NodeService, ProjectAssetService, ProjectService);
+ this.numColumnsChangeSubscription = this.numColumnsChange
+ .pipe(debounceTime(1000), distinctUntilChanged())
+ .subscribe(() => {
+ this.tableNumColumnsChanged();
+ });
+ this.numRowsChangeSubscription = this.numRowsChange
+ .pipe(debounceTime(1000), distinctUntilChanged())
+ .subscribe(() => {
+ this.tableNumRowsChanged();
+ });
+ this.globalCellSizeChangeSubscription = this.globalCellSizeChange
+ .pipe(debounceTime(1000), distinctUntilChanged())
+ .subscribe(() => {
+ this.componentChanged();
+ });
+ this.inputChangeSubscription = this.inputChange
+ .pipe(debounceTime(1000), distinctUntilChanged())
+ .subscribe(() => {
+ this.componentChanged();
+ });
+ }
+
+ ngOnInit() {
+ super.ngOnInit();
+ this.columnCellSizes = this.parseColumnCellSizes(this.componentContent);
+ }
+
+ ngOnDestroy() {
+ super.ngOnDestroy();
+ this.unsubscribeAll();
+ }
+
+ unsubscribeAll() {
+ this.numColumnsChangeSubscription.unsubscribe();
+ this.numRowsChangeSubscription.unsubscribe();
+ this.globalCellSizeChangeSubscription.unsubscribe();
+ this.inputChangeSubscription.unsubscribe();
+ }
+
+ tableNumRowsChanged(): void {
+ const oldValue = this.getNumRowsInTableData();
+ const newValue = this.authoringComponentContent.numRows;
+ if (newValue < oldValue) {
+ if (this.areRowsAfterEmpty(newValue)) {
+ this.tableSizeChanged();
+ } else {
+ if (confirm($localize`Are you sure you want to decrease the number of rows?`)) {
+ this.tableSizeChanged();
+ } else {
+ this.authoringComponentContent.numRows = oldValue;
+ }
+ }
+ } else {
+ this.tableSizeChanged();
+ }
+ }
+
+ areRowsAfterEmpty(rowIndex: number): boolean {
+ const oldNumRows = this.getNumRowsInTableData();
+ for (let r = rowIndex; r < oldNumRows; r++) {
+ if (!this.isRowEmpty(r)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ isRowEmpty(rowIndex: number): boolean {
+ const tableData = this.authoringComponentContent.tableData;
+ for (const cell of tableData[rowIndex]) {
+ if (cell.text != null && cell.text != '') {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ tableNumColumnsChanged(): void {
+ const oldValue = this.getNumColumnsInTableData();
+ const newValue = this.authoringComponentContent.numColumns;
+ if (newValue < oldValue) {
+ if (this.areColumnsAfterEmpty(newValue)) {
+ this.tableSizeChanged();
+ } else {
+ if (confirm($localize`Are you sure you want to decrease the number of columns?`)) {
+ this.tableSizeChanged();
+ } else {
+ this.authoringComponentContent.numColumns = oldValue;
+ }
+ }
+ } else {
+ this.tableSizeChanged();
+ }
+ }
+
+ areColumnsAfterEmpty(columnIndex: number): boolean {
+ const oldNumColumns = this.getNumColumnsInTableData();
+ for (let c = columnIndex; c < oldNumColumns; c++) {
+ if (!this.isColumnEmpty(c)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ isColumnEmpty(columnIndex: number): boolean {
+ for (const row of this.authoringComponentContent.tableData) {
+ const cell = row[columnIndex];
+ if (cell.text != null && cell.text != '') {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ tableSizeChanged(): void {
+ this.authoringComponentContent.tableData = this.getUpdatedTable(
+ this.authoringComponentContent.numRows,
+ this.authoringComponentContent.numColumns
+ );
+ this.componentChanged();
+ }
+
+ /**
+ * Create a table with the given dimensions. Populate the cells with the cells from the old table.
+ * @param newNumRows the number of rows in the new table
+ * @param newNumColumns the number of columns in the new table
+ * @returns a new table
+ */
+ getUpdatedTable(newNumRows: number, newNumColumns: number): any {
+ const newTable = [];
+ for (let r = 0; r < newNumRows; r++) {
+ const newRow = [];
+ for (let c = 0; c < newNumColumns; c++) {
+ let cell = this.getCellObjectFromTableData(c, r);
+ if (cell == null) {
+ cell = this.createEmptyCell();
+ }
+ newRow.push(cell);
+ }
+ newTable.push(newRow);
+ }
+ return newTable;
+ }
+
+ /**
+ * Get the cell object at the given x, y location
+ * @param x the column number (zero indexed)
+ * @param y the row number (zero indexed)
+ * @returns the cell at the given x, y location or null if there is none
+ */
+ getCellObjectFromTableData(x: number, y: number): any {
+ let cellObject = null;
+ const tableData = this.authoringComponentContent.tableData;
+ if (tableData != null) {
+ const row = tableData[y];
+ if (row != null) {
+ cellObject = row[x];
+ }
+ }
+ return cellObject;
+ }
+
+ createEmptyCell(): any {
+ return {
+ text: '',
+ editable: true,
+ size: null
+ };
+ }
+
+ insertRow(y: number): void {
+ const tableData = this.authoringComponentContent.tableData;
+ const newRow = [];
+ const numColumns = this.authoringComponentContent.numColumns;
+ for (let c = 0; c < numColumns; c++) {
+ const newCell = this.createEmptyCell();
+ const cellSize = this.columnCellSizes[c];
+ if (cellSize != null) {
+ newCell.size = cellSize;
+ }
+ newRow.push(newCell);
+ }
+ tableData.splice(y, 0, newRow);
+ this.authoringComponentContent.numRows++;
+ this.componentChanged();
+ }
+
+ deleteRow(y: number): void {
+ if (confirm($localize`Are you sure you want to delete this row?`)) {
+ const tableData = this.authoringComponentContent.tableData;
+ if (tableData != null) {
+ tableData.splice(y, 1);
+ this.authoringComponentContent.numRows--;
+ }
+ this.componentChanged();
+ }
+ }
+
+ insertColumn(x: number): void {
+ const tableData = this.authoringComponentContent.tableData;
+ const numRows = this.authoringComponentContent.numRows;
+ for (let r = 0; r < numRows; r++) {
+ const tempRow = tableData[r];
+ if (tempRow != null) {
+ const newCell = this.createEmptyCell();
+ tempRow.splice(x, 0, newCell);
+ }
+ }
+ this.authoringComponentContent.numColumns++;
+ this.parseColumnCellSizes(this.authoringComponentContent);
+ this.componentChanged();
+ }
+
+ deleteColumn(x: number): void {
+ if (confirm($localize`Are you sure you want to delete this column?`)) {
+ const tableData = this.authoringComponentContent.tableData;
+ const numRows = this.authoringComponentContent.numRows;
+ for (let r = 0; r < numRows; r++) {
+ const tempRow = tableData[r];
+ if (tempRow != null) {
+ tempRow.splice(x, 1);
+ }
+ }
+ this.authoringComponentContent.numColumns--;
+ this.parseColumnCellSizes(this.authoringComponentContent);
+ this.componentChanged();
+ }
+ }
+
+ /**
+ * Get the number of rows in the table data. This is slightly different from just getting the
+ * numRows field in the component content. Usually the number of rows will be the same. In some
+ * cases it can be different such as during authoring immediately after the author changes the
+ * number of rows using the number of rows input.
+ * @return {number} The number of rows in the table data.
+ */
+ getNumRowsInTableData(): number {
+ return this.authoringComponentContent.tableData.length;
+ }
+
+ /**
+ * Get the number of columns in the table data. This is slightly different from just getting the
+ * numColumns field in the component content. Usually the number of columns will be the same. In
+ * some cases it can be different such as during authoring immediately after the author changes
+ * the number of columns using the number of columns input.
+ * @return {number} The number of columns in the table data.
+ */
+ getNumColumnsInTableData(): number {
+ const tableData = this.authoringComponentContent.tableData;
+ if (tableData.length > 0) {
+ return tableData[0].length;
+ }
+ return 0;
+ }
+
+ setAllCellsUneditable(): void {
+ this.setAllCellsIsEditable(false);
+ this.componentChanged();
+ }
+
+ setAllCellsEditable(): void {
+ this.setAllCellsIsEditable(true);
+ this.componentChanged();
+ }
+
+ setAllCellsIsEditable(isEditable: boolean): void {
+ const tableData = this.authoringComponentContent.tableData;
+ for (let r = 0; r < tableData.length; r++) {
+ const row = tableData[r];
+ for (let c = 0; c < row.length; c++) {
+ const cell = row[c];
+ cell.editable = isEditable;
+ }
+ }
+ }
+
+ /**
+ * Parse the column cell sizes. We will get the column cell sizes by looking at the size value of
+ * each cell in the first row.
+ * @param componentContent the component content
+ */
+ parseColumnCellSizes(componentContent: any): any {
+ const columnCellSizes = {};
+ const tableData = componentContent.tableData;
+ const firstRow = tableData[0];
+ if (firstRow != null) {
+ for (let x = 0; x < firstRow.length; x++) {
+ const cell = firstRow[x];
+ columnCellSizes[x] = cell.size;
+ }
+ }
+ return columnCellSizes;
+ }
+
+ columnSizeChanged(index: number): void {
+ let cellSize = this.columnCellSizes[index];
+ if (cellSize == '') {
+ cellSize = null;
+ }
+ this.setColumnCellSizes(index, cellSize);
+ }
+
+ setColumnCellSizes(column: number, size: number): void {
+ const tableData = this.authoringComponentContent.tableData;
+ for (let r = 0; r < tableData.length; r++) {
+ const row = tableData[r];
+ const cell = row[column];
+ if (cell != null) {
+ cell.size = size;
+ }
+ }
+ this.componentChanged();
+ }
+
+ automaticallySetConnectedComponentFieldsIfPossible(connectedComponent) {
+ if (connectedComponent.type === 'importWork' && connectedComponent.action == null) {
+ connectedComponent.action = 'merge';
+ } else if (connectedComponent.type === 'showWork') {
+ connectedComponent.action = null;
+ }
+ }
+
+ connectedComponentTypeChanged(connectedComponent) {
+ this.automaticallySetConnectedComponentFieldsIfPossible(connectedComponent);
+ this.componentChanged();
+ }
+}
diff --git a/src/main/webapp/wise5/components/table/tableAuthoring.ts b/src/main/webapp/wise5/components/table/tableAuthoring.ts
deleted file mode 100644
index beef4e7fef..0000000000
--- a/src/main/webapp/wise5/components/table/tableAuthoring.ts
+++ /dev/null
@@ -1,428 +0,0 @@
-'use strict';
-
-import { Directive } from '@angular/core';
-import { EditComponentController } from '../../authoringTool/components/editComponentController';
-
-@Directive()
-class TableAuthoringController extends EditComponentController {
- columnCellSizes: any;
-
- static $inject = [
- '$filter',
- 'ConfigService',
- 'NodeService',
- 'NotificationService',
- 'ProjectAssetService',
- 'ProjectService',
- 'UtilService'
- ];
-
- constructor(
- $filter,
- ConfigService,
- NodeService,
- NotificationService,
- ProjectAssetService,
- ProjectService,
- UtilService
- ) {
- super(
- $filter,
- ConfigService,
- NodeService,
- NotificationService,
- ProjectAssetService,
- ProjectService,
- UtilService
- );
- }
-
- $onInit() {
- super.$onInit();
- this.columnCellSizes = this.parseColumnCellSizes(this.componentContent);
- }
-
- tableNumRowsChanged(oldValue: number): void {
- if (this.authoringComponentContent.numRows < oldValue) {
- if (this.areRowsAfterEmpty(this.authoringComponentContent.numRows)) {
- this.tableSizeChanged();
- } else {
- if (confirm(this.$translate('table.areYouSureYouWantToDecreaseTheNumberOfRows'))) {
- this.tableSizeChanged();
- } else {
- this.authoringComponentContent.numRows = oldValue;
- }
- }
- } else {
- this.tableSizeChanged();
- }
- }
-
- /**
- * Determine if the rows after the given index are empty.
- * @param rowIndex The index of the row to start checking at. This value is zero indexed.
- * @return {boolean} True if the row at the given index and all the rows after are empty.
- * False if the row at the given index or any row after the row index is not empty.
- */
- areRowsAfterEmpty(rowIndex: number): boolean {
- const oldNumRows = this.getNumRowsInTableData();
- for (let r = rowIndex; r < oldNumRows; r++) {
- if (!this.isRowEmpty(r)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Determine if a row has cells that are all empty string.
- * @param rowIndex The row index. This value is zero indexed.
- * @returns {boolean} True if the text in all the cells in the row are empty string.
- * False if the text in any cell in the row is not empty string.
- */
- isRowEmpty(rowIndex: number): boolean {
- const tableData = this.authoringComponentContent.tableData;
- for (const cell of tableData[rowIndex]) {
- if (cell.text != null && cell.text != '') {
- return false;
- }
- }
- return true;
- }
-
- /**
- * The author has changed the number of columns.
- * @param oldValue The previous number of columns.
- */
- tableNumColumnsChanged(oldValue: number): void {
- if (this.authoringComponentContent.numColumns < oldValue) {
- // the author is reducing the number of columns
- if (this.areColumnsAfterEmpty(this.authoringComponentContent.numColumns)) {
- // the columns that we will delete are empty so we will remove the columns
- this.tableSizeChanged();
- } else {
- if (confirm(this.$translate('table.areYouSureYouWantToDecreaseTheNumberOfColumns'))) {
- this.tableSizeChanged();
- } else {
- this.authoringComponentContent.numColumns = oldValue;
- }
- }
- } else {
- // the author is increasing the number of columns
- this.tableSizeChanged();
- }
- }
-
- /**
- * Determine if the columns after the given index are empty.
- * @param columnIndex The index of the column to start checking at. This value is zero indexed.
- * @return {boolean} True if the column at the given index and all the columns after are empty.
- * False if the column at the given index or any column after the column index is not empty.
- */
- areColumnsAfterEmpty(columnIndex: number): boolean {
- const oldNumColumns = this.getNumColumnsInTableData();
- for (let c = columnIndex; c < oldNumColumns; c++) {
- if (!this.isColumnEmpty(c)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Determine if a column has cells that are all empty string.
- * @param columnIndex The column index. This value is zero indexed.
- * @returns {boolean} True if the text in all the cells in the column are empty string.
- * False if the text in any cell in the column is not empty string.
- */
- isColumnEmpty(columnIndex: number): boolean {
- for (const row of this.authoringComponentContent.tableData) {
- const cell = row[columnIndex];
- if (cell.text != null && cell.text != '') {
- return false;
- }
- }
- return true;
- }
-
- /**
- * The table size has changed in the authoring view so we will update it
- */
- tableSizeChanged(): void {
- this.authoringComponentContent.tableData = this.getUpdatedTableSize(
- this.authoringComponentContent.numRows,
- this.authoringComponentContent.numColumns
- );
- this.componentChanged();
- }
-
- /**
- * Create a table with the given dimensions. Populate the cells with
- * the cells from the old table.
- * @param newNumRows the number of rows in the new table
- * @param newNumColumns the number of columns in the new table
- * @returns a new table
- */
- getUpdatedTableSize(newNumRows: number, newNumColumns: number): any {
- const newTable = [];
- for (let r = 0; r < newNumRows; r++) {
- const newRow = [];
- for (let c = 0; c < newNumColumns; c++) {
- let cell = this.getCellObjectFromComponentContent(c, r);
- if (cell == null) {
- cell = this.createEmptyCell();
- }
- newRow.push(cell);
- }
- newTable.push(newRow);
- }
- return newTable;
- }
-
- /**
- * Get the cell object at the given x, y location
- * @param x the column number (zero indexed)
- * @param y the row number (zero indexed)
- * @returns the cell at the given x, y location or null if there is none
- */
- getCellObjectFromComponentContent(x: number, y: number): any {
- let cellObject = null;
- const tableData = this.authoringComponentContent.tableData;
- if (tableData != null) {
- const row = tableData[y];
- if (row != null) {
- cellObject = row[x];
- }
- }
- return cellObject;
- }
-
- createEmptyCell(): any {
- return {
- text: '',
- editable: true,
- size: null
- };
- }
-
- /**
- * Insert a row into the table from the authoring view
- * @param y the row number to insert at
- */
- insertRow(y: number): void {
- const tableData = this.authoringComponentContent.tableData;
- if (tableData != null) {
- const newRow = [];
- const numColumns = this.authoringComponentContent.numColumns;
- for (let c = 0; c < numColumns; c++) {
- const newCell = this.createEmptyCell();
- const cellSize = this.columnCellSizes[c];
- if (cellSize != null) {
- newCell.size = cellSize;
- }
- newRow.push(newCell);
- }
- tableData.splice(y, 0, newRow);
- this.authoringComponentContent.numRows++;
- }
- this.componentChanged();
- }
-
- /**
- * Delete a row in the table from the authoring view
- * @param y the row number to delete
- */
- deleteRow(y: number): void {
- if (confirm(this.$translate('table.areYouSureYouWantToDeleteThisRow'))) {
- const tableData = this.authoringComponentContent.tableData;
- if (tableData != null) {
- tableData.splice(y, 1);
- this.authoringComponentContent.numRows--;
- }
- this.componentChanged();
- }
- }
-
- /**
- * Insert a column into the table from the authoring view
- * @param x the column number to insert at
- */
- insertColumn(x: number): void {
- const tableData = this.authoringComponentContent.tableData;
- if (tableData != null) {
- const numRows = this.authoringComponentContent.numRows;
- for (let r = 0; r < numRows; r++) {
- const tempRow = tableData[r];
- if (tempRow != null) {
- const newCell = this.createEmptyCell();
- tempRow.splice(x, 0, newCell);
- }
- }
- this.authoringComponentContent.numColumns++;
- this.parseColumnCellSizes(this.authoringComponentContent);
- }
- this.componentChanged();
- }
-
- /**
- * Delete a column in the table from the authoring view
- * @param x the column number to delete
- */
- deleteColumn(x: number): void {
- if (confirm(this.$translate('table.areYouSureYouWantToDeleteThisColumn'))) {
- const tableData = this.authoringComponentContent.tableData;
- if (tableData != null) {
- const numRows = this.authoringComponentContent.numRows;
- for (let r = 0; r < numRows; r++) {
- const tempRow = tableData[r];
- if (tempRow != null) {
- tempRow.splice(x, 1);
- }
- }
- this.authoringComponentContent.numColumns--;
- this.parseColumnCellSizes(this.authoringComponentContent);
- }
- this.componentChanged();
- }
- }
-
- /**
- * Get the number of rows in the table data. This is slightly different from
- * just getting the numRows field in the component content. Usually the
- * number of rows will be the same. In some cases it can be different
- * such as during authoring immediately after the author changes the number
- * of rows using the number of rows input.
- * @return {number} The number of rows in the table data.
- */
- getNumRowsInTableData(): number {
- return this.authoringComponentContent.tableData.length;
- }
-
- /**
- * Get the number of columns in the table data. This is slightly different from
- * just getting the numColumns field in the component content. Usually the
- * number of columns will be the same. In some cases it can be different
- * such as during authoring immediately after the author changes the number
- * of columns using the number of columns input.
- * @return {number} The number of columns in the table data.
- */
- getNumColumnsInTableData(): number {
- const tableData = this.authoringComponentContent.tableData;
- if (tableData.length > 0) {
- return tableData[0].length;
- }
- return 0;
- }
-
- makeAllCellsUneditable(): void {
- const tableData = this.authoringComponentContent.tableData;
- if (tableData != null) {
- for (let r = 0; r < tableData.length; r++) {
- const row = tableData[r];
- if (row != null) {
- for (let c = 0; c < row.length; c++) {
- const cell = row[c];
- if (cell != null) {
- cell.editable = false;
- }
- }
- }
- }
- }
- this.componentChanged();
- }
-
- makeAllCellsEditable(): void {
- const tableData = this.authoringComponentContent.tableData;
- if (tableData != null) {
- for (let r = 0; r < tableData.length; r++) {
- const row = tableData[r];
- if (row != null) {
- for (let c = 0; c < row.length; c++) {
- const cell = row[c];
- if (cell != null) {
- cell.editable = true;
- }
- }
- }
- }
- }
- this.componentChanged();
- }
-
- /**
- * Parse the column cell sizes. We will get the column cell sizes by looking
- * at size value of each column in the first row.
- * @param componentContent the component content
- */
- parseColumnCellSizes(componentContent: any): any {
- const columnCellSizes = {};
- const tableData = componentContent.tableData;
- if (tableData != null) {
- const firstRow = tableData[0];
- if (firstRow != null) {
- for (let x = 0; x < firstRow.length; x++) {
- const cell = firstRow[x];
- columnCellSizes[x] = cell.size;
- }
- }
- }
- return columnCellSizes;
- }
-
- columnSizeChanged(index: number): void {
- if (index != null) {
- let cellSize = this.columnCellSizes[index];
- if (cellSize == '') {
- cellSize = null;
- }
- this.setColumnCellSizes(index, cellSize);
- }
- }
-
- /**
- * Set the cell sizes for all the cells in a column
- * @param column the column number
- * @param size the cell size
- */
- setColumnCellSizes(column: number, size: number): void {
- const tableData = this.authoringComponentContent.tableData;
- if (tableData != null) {
- for (let r = 0; r < tableData.length; r++) {
- const row = tableData[r];
- if (row != null) {
- const cell = row[column];
- if (cell != null) {
- cell.size = size;
- }
- }
- }
- }
- this.componentChanged();
- }
-
- automaticallySetConnectedComponentFieldsIfPossible(connectedComponent) {
- if (connectedComponent.type === 'importWork' && connectedComponent.action == null) {
- connectedComponent.action = 'merge';
- } else if (connectedComponent.type === 'showWork') {
- connectedComponent.action = null;
- }
- }
-
- connectedComponentTypeChanged(connectedComponent) {
- this.automaticallySetConnectedComponentFieldsIfPossible(connectedComponent);
- this.componentChanged();
- }
-}
-
-const TableAuthoring = {
- bindings: {
- nodeId: '@',
- componentId: '@'
- },
- controller: TableAuthoringController,
- controllerAs: 'tableController',
- templateUrl: 'wise5/components/table/authoring.html'
-};
-
-export default TableAuthoring;
diff --git a/src/main/webapp/wise5/components/table/tableAuthoringComponentModule.ts b/src/main/webapp/wise5/components/table/tableAuthoringComponentModule.ts
index 60f9c96360..c202b806cd 100644
--- a/src/main/webapp/wise5/components/table/tableAuthoringComponentModule.ts
+++ b/src/main/webapp/wise5/components/table/tableAuthoringComponentModule.ts
@@ -2,14 +2,17 @@
import * as angular from 'angular';
import { TableService } from './tableService';
-import { downgradeInjectable } from '@angular/upgrade/static';
-import TableAuthoring from './tableAuthoring';
+import { downgradeComponent, downgradeInjectable } from '@angular/upgrade/static';
import { EditTableAdvancedComponent } from './edit-table-advanced/edit-table-advanced.component';
+import { TableAuthoring } from './table-authoring/table-authoring.component';
const tableAuthoringComponentModule = angular
.module('tableAuthoringComponentModule', ['pascalprecht.translate'])
.service('TableService', downgradeInjectable(TableService))
- .component('tableAuthoring', TableAuthoring)
+ .directive(
+ 'tableAuthoring',
+ downgradeComponent({ component: TableAuthoring }) as angular.IDirectiveFactory
+ )
.component('editTableAdvanced', EditTableAdvancedComponent)
.config([
'$translatePartialLoaderProvider',
From 2c44f999f4d7c1055baa9cc2d7e3a61dc7034125 Mon Sep 17 00:00:00 2001
From: Geoffrey Kwan
Date: Wed, 27 Jan 2021 15:50:59 -0500
Subject: [PATCH 09/13] Upgraded Discussion authoring to Angular. #2908
---
.../src/app/teacher-hybrid-angular.module.ts | 2 +
src/main/webapp/site/src/messages.xlf | 22 +++++++++
.../components/discussion/authoring.html | 28 -----------
.../discussion-authoring.component.html | 28 +++++++++++
.../discussion-authoring.component.scss | 8 +++
.../discussion-authoring.component.ts | 24 +++++++++
.../discussion/discussionAuthoring.ts | 49 -------------------
.../discussionAuthoringComponentModule.ts | 9 ++--
.../discussion/discussionService.ts | 2 +-
9 files changed, 91 insertions(+), 81 deletions(-)
delete mode 100644 src/main/webapp/wise5/components/discussion/authoring.html
create mode 100644 src/main/webapp/wise5/components/discussion/discussion-authoring/discussion-authoring.component.html
create mode 100644 src/main/webapp/wise5/components/discussion/discussion-authoring/discussion-authoring.component.scss
create mode 100644 src/main/webapp/wise5/components/discussion/discussion-authoring/discussion-authoring.component.ts
delete mode 100644 src/main/webapp/wise5/components/discussion/discussionAuthoring.ts
diff --git a/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts b/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts
index 6a5134318d..276c4afc0c 100644
--- a/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts
+++ b/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts
@@ -52,6 +52,7 @@ import { ConceptMapAuthoring } from '../../../wise5/components/conceptMap/concep
import { DrawAuthoring } from '../../../wise5/components/draw/draw-authoring/draw-authoring.component';
import { MatchAuthoring } from '../../../wise5/components/match/match-authoring/match-authoring.component';
import { LabelAuthoring } from '../../../wise5/components/label/label-authoring/label-authoring.component';
+import { DiscussionAuthoring } from '../../../wise5/components/discussion/discussion-authoring/discussion-authoring.component';
@NgModule({
declarations: [
@@ -65,6 +66,7 @@ import { LabelAuthoring } from '../../../wise5/components/label/label-authoring/
ComponentSelectComponent,
ConceptMapAuthoring,
DrawAuthoring,
+ DiscussionAuthoring,
EditComponentRubricComponent,
EditComponentJsonComponent,
EditComponentMaxScoreComponent,
diff --git a/src/main/webapp/site/src/messages.xlf b/src/main/webapp/site/src/messages.xlf
index c7e6ac4b89..42884991b0 100644
--- a/src/main/webapp/site/src/messages.xlf
+++ b/src/main/webapp/site/src/messages.xlf
@@ -6024,6 +6024,10 @@
../../wise5/components/draw/draw-authoring/draw-authoring.component.html
2
+
+ ../../wise5/components/discussion/discussion-authoring/discussion-authoring.component.html
+ 2
+
../../wise5/components/label/label-authoring/label-authoring.component.html
2
@@ -6051,6 +6055,10 @@
../../wise5/components/draw/draw-authoring/draw-authoring.component.html
6
+
+ ../../wise5/components/discussion/discussion-authoring/discussion-authoring.component.html
+ 6
+
../../wise5/components/label/label-authoring/label-authoring.component.html
6
@@ -6653,6 +6661,20 @@
285
+
+ Students can upload and use images in their posts
+
+ ../../wise5/components/discussion/discussion-authoring/discussion-authoring.component.html
+ 17
+
+
+
+ Students must create a post before viewing classmates' posts
+
+ ../../wise5/components/discussion/discussion-authoring/discussion-authoring.component.html
+ 26
+
+
Rubric
diff --git a/src/main/webapp/wise5/components/discussion/authoring.html b/src/main/webapp/wise5/components/discussion/authoring.html
deleted file mode 100644
index 1c169b45ce..0000000000
--- a/src/main/webapp/wise5/components/discussion/authoring.html
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
- {{ ::'PROMPT' | translate }}
-
-
-
-
- {{ ::'discussion.allowUploadedImagesInPosts' | translate }}
-
-
-
-
-
- {{ ::'discussion.gateClassmateResponses' | translate }}
-
-
-
-
diff --git a/src/main/webapp/wise5/components/discussion/discussion-authoring/discussion-authoring.component.html b/src/main/webapp/wise5/components/discussion/discussion-authoring/discussion-authoring.component.html
new file mode 100644
index 0000000000..d457e04955
--- /dev/null
+++ b/src/main/webapp/wise5/components/discussion/discussion-authoring/discussion-authoring.component.html
@@ -0,0 +1,28 @@
+
+ Prompt
+
+
+
+
+ Students can upload and use images in their posts
+
+
+
+
+ Students must create a post before viewing classmates' posts
+
+
\ No newline at end of file
diff --git a/src/main/webapp/wise5/components/discussion/discussion-authoring/discussion-authoring.component.scss b/src/main/webapp/wise5/components/discussion/discussion-authoring/discussion-authoring.component.scss
new file mode 100644
index 0000000000..7221a74cf4
--- /dev/null
+++ b/src/main/webapp/wise5/components/discussion/discussion-authoring/discussion-authoring.component.scss
@@ -0,0 +1,8 @@
+.prompt {
+ width: 100%;
+}
+
+.checkbox-container {
+ margin-top: 5px;
+ margin-bottom: 15px;
+}
\ No newline at end of file
diff --git a/src/main/webapp/wise5/components/discussion/discussion-authoring/discussion-authoring.component.ts b/src/main/webapp/wise5/components/discussion/discussion-authoring/discussion-authoring.component.ts
new file mode 100644
index 0000000000..ce81455ece
--- /dev/null
+++ b/src/main/webapp/wise5/components/discussion/discussion-authoring/discussion-authoring.component.ts
@@ -0,0 +1,24 @@
+'use strict';
+
+import { Component } from '@angular/core';
+import { ProjectAssetService } from '../../../../site/src/app/services/projectAssetService';
+import { ComponentAuthoring } from '../../../authoringTool/components/component-authoring.component';
+import { ConfigService } from '../../../services/configService';
+import { NodeService } from '../../../services/nodeService';
+import { TeacherProjectService } from '../../../services/teacherProjectService';
+
+@Component({
+ selector: 'discussion-authoring',
+ templateUrl: 'discussion-authoring.component.html',
+ styleUrls: ['discussion-authoring.component.scss']
+})
+export class DiscussionAuthoring extends ComponentAuthoring {
+ constructor(
+ protected ConfigService: ConfigService,
+ protected NodeService: NodeService,
+ protected ProjectAssetService: ProjectAssetService,
+ protected ProjectService: TeacherProjectService
+ ) {
+ super(ConfigService, NodeService, ProjectAssetService, ProjectService);
+ }
+}
diff --git a/src/main/webapp/wise5/components/discussion/discussionAuthoring.ts b/src/main/webapp/wise5/components/discussion/discussionAuthoring.ts
deleted file mode 100644
index d30b4b0c2f..0000000000
--- a/src/main/webapp/wise5/components/discussion/discussionAuthoring.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-'use strict';
-
-import { Directive } from '@angular/core';
-import { EditComponentController } from '../../authoringTool/components/editComponentController';
-
-@Directive()
-class DiscussionAuthoringController extends EditComponentController {
- static $inject = [
- '$filter',
- 'ConfigService',
- 'NodeService',
- 'NotificationService',
- 'ProjectAssetService',
- 'ProjectService',
- 'UtilService'
- ];
-
- constructor(
- $filter,
- ConfigService,
- NodeService,
- NotificationService,
- ProjectAssetService,
- ProjectService,
- UtilService
- ) {
- super(
- $filter,
- ConfigService,
- NodeService,
- NotificationService,
- ProjectAssetService,
- ProjectService,
- UtilService
- );
- }
-}
-
-const DiscussionAuthoring = {
- bindings: {
- nodeId: '@',
- componentId: '@'
- },
- controller: DiscussionAuthoringController,
- controllerAs: 'discussionController',
- templateUrl: 'wise5/components/discussion/authoring.html'
-};
-
-export default DiscussionAuthoring;
diff --git a/src/main/webapp/wise5/components/discussion/discussionAuthoringComponentModule.ts b/src/main/webapp/wise5/components/discussion/discussionAuthoringComponentModule.ts
index 77bf783b60..9acf77bcbe 100644
--- a/src/main/webapp/wise5/components/discussion/discussionAuthoringComponentModule.ts
+++ b/src/main/webapp/wise5/components/discussion/discussionAuthoringComponentModule.ts
@@ -1,16 +1,19 @@
'use strict';
import * as angular from 'angular';
-import { downgradeInjectable } from '@angular/upgrade/static';
+import { downgradeComponent, downgradeInjectable } from '@angular/upgrade/static';
import { DiscussionService } from './discussionService';
-import DiscussionAuthoring from './discussionAuthoring';
import { EditDiscussionAdvancedComponent } from './edit-discussion-advanced/edit-discussion-advanced.component';
+import { DiscussionAuthoring } from './discussion-authoring/discussion-authoring.component';
const discussionAuthoringComponentModule = angular
.module('discussionAuthoringComponentModule', ['pascalprecht.translate'])
.service('DiscussionService', downgradeInjectable(DiscussionService))
- .component('discussionAuthoring', DiscussionAuthoring)
.component('editDiscussionAdvanced', EditDiscussionAdvancedComponent)
+ .directive(
+ 'discussionAuthoring',
+ downgradeComponent({ component: DiscussionAuthoring }) as angular.IDirectiveFactory
+ )
.config([
'$translatePartialLoaderProvider',
($translatePartialLoaderProvider) => {
diff --git a/src/main/webapp/wise5/components/discussion/discussionService.ts b/src/main/webapp/wise5/components/discussion/discussionService.ts
index 7249536a45..78467bf01e 100644
--- a/src/main/webapp/wise5/components/discussion/discussionService.ts
+++ b/src/main/webapp/wise5/components/discussion/discussionService.ts
@@ -39,7 +39,7 @@ export class DiscussionService extends ComponentService {
createComponent() {
const component: any = super.createComponent();
component.type = 'Discussion';
- component.prompt = this.getTranslation('ENTER_PROMPT_HERE');
+ component.prompt = '';
component.isStudentAttachmentEnabled = true;
component.gateClassmateResponses = true;
return component;
From 6e50560b5e4fc2bf7ca2314be4c9182b346e7156 Mon Sep 17 00:00:00 2001
From: Hiroki Terashima
Date: Thu, 28 Jan 2021 10:36:32 -0800
Subject: [PATCH 10/13] Cleaned up table authoring code. #2902
---
.../table-authoring/table-authoring.component.ts | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/src/main/webapp/wise5/components/table/table-authoring/table-authoring.component.ts b/src/main/webapp/wise5/components/table/table-authoring/table-authoring.component.ts
index 46e7b3c3c6..a1e97f3052 100644
--- a/src/main/webapp/wise5/components/table/table-authoring/table-authoring.component.ts
+++ b/src/main/webapp/wise5/components/table/table-authoring/table-authoring.component.ts
@@ -104,7 +104,7 @@ export class TableAuthoring extends ComponentAuthoring {
isRowEmpty(rowIndex: number): boolean {
const tableData = this.authoringComponentContent.tableData;
for (const cell of tableData[rowIndex]) {
- if (cell.text != null && cell.text != '') {
+ if (!this.isEmpty(cell.text)) {
return false;
}
}
@@ -142,13 +142,17 @@ export class TableAuthoring extends ComponentAuthoring {
isColumnEmpty(columnIndex: number): boolean {
for (const row of this.authoringComponentContent.tableData) {
const cell = row[columnIndex];
- if (cell.text != null && cell.text != '') {
+ if (!this.isEmpty(cell.text)) {
return false;
}
}
return true;
}
+ isEmpty(txt: string): boolean {
+ return txt == null || txt == '';
+ }
+
tableSizeChanged(): void {
this.authoringComponentContent.tableData = this.getUpdatedTable(
this.authoringComponentContent.numRows,
@@ -301,11 +305,8 @@ export class TableAuthoring extends ComponentAuthoring {
}
setAllCellsIsEditable(isEditable: boolean): void {
- const tableData = this.authoringComponentContent.tableData;
- for (let r = 0; r < tableData.length; r++) {
- const row = tableData[r];
- for (let c = 0; c < row.length; c++) {
- const cell = row[c];
+ for (const row of this.authoringComponentContent.tableData) {
+ for (const cell of row) {
cell.editable = isEditable;
}
}
From d125cbf84db1029e89aac7aa9b55da050d79a68b Mon Sep 17 00:00:00 2001
From: Geoffrey Kwan
Date: Fri, 29 Jan 2021 15:03:15 -0500
Subject: [PATCH 11/13] Cleaned up some Table authoring code. #2902
---
.../table-authoring.component.html | 4 +++
.../table-authoring.component.ts | 26 ++++++++-----------
2 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/src/main/webapp/wise5/components/table/table-authoring/table-authoring.component.html b/src/main/webapp/wise5/components/table/table-authoring/table-authoring.component.html
index 5995f387dd..9fcbfba3af 100644
--- a/src/main/webapp/wise5/components/table/table-authoring/table-authoring.component.html
+++ b/src/main/webapp/wise5/components/table/table-authoring/table-authoring.component.html
@@ -13,6 +13,7 @@
Columns
@@ -20,6 +21,7 @@
Rows
@@ -27,6 +29,7 @@
Global Cell Size
@@ -152,6 +155,7 @@
Column Cell Size
diff --git a/src/main/webapp/wise5/components/table/table-authoring/table-authoring.component.ts b/src/main/webapp/wise5/components/table/table-authoring/table-authoring.component.ts
index a1e97f3052..0d4cfa8f31 100644
--- a/src/main/webapp/wise5/components/table/table-authoring/table-authoring.component.ts
+++ b/src/main/webapp/wise5/components/table/table-authoring/table-authoring.component.ts
@@ -209,7 +209,7 @@ export class TableAuthoring extends ComponentAuthoring {
};
}
- insertRow(y: number): void {
+ insertRow(rowIndex: number): void {
const tableData = this.authoringComponentContent.tableData;
const newRow = [];
const numColumns = this.authoringComponentContent.numColumns;
@@ -221,46 +221,42 @@ export class TableAuthoring extends ComponentAuthoring {
}
newRow.push(newCell);
}
- tableData.splice(y, 0, newRow);
+ tableData.splice(rowIndex, 0, newRow);
this.authoringComponentContent.numRows++;
this.componentChanged();
}
- deleteRow(y: number): void {
+ deleteRow(rowIndex: number): void {
if (confirm($localize`Are you sure you want to delete this row?`)) {
const tableData = this.authoringComponentContent.tableData;
if (tableData != null) {
- tableData.splice(y, 1);
+ tableData.splice(rowIndex, 1);
this.authoringComponentContent.numRows--;
}
this.componentChanged();
}
}
- insertColumn(x: number): void {
+ insertColumn(columnIndex: number): void {
const tableData = this.authoringComponentContent.tableData;
const numRows = this.authoringComponentContent.numRows;
for (let r = 0; r < numRows; r++) {
- const tempRow = tableData[r];
- if (tempRow != null) {
- const newCell = this.createEmptyCell();
- tempRow.splice(x, 0, newCell);
- }
+ const row = tableData[r];
+ const newCell = this.createEmptyCell();
+ row.splice(columnIndex, 0, newCell);
}
this.authoringComponentContent.numColumns++;
this.parseColumnCellSizes(this.authoringComponentContent);
this.componentChanged();
}
- deleteColumn(x: number): void {
+ deleteColumn(columnIndex: number): void {
if (confirm($localize`Are you sure you want to delete this column?`)) {
const tableData = this.authoringComponentContent.tableData;
const numRows = this.authoringComponentContent.numRows;
for (let r = 0; r < numRows; r++) {
- const tempRow = tableData[r];
- if (tempRow != null) {
- tempRow.splice(x, 1);
- }
+ const row = tableData[r];
+ row.splice(columnIndex, 1);
}
this.authoringComponentContent.numColumns--;
this.parseColumnCellSizes(this.authoringComponentContent);
From ffd530cb511d3e3609b9464bbf6b0b26b05941d8 Mon Sep 17 00:00:00 2001
From: breity
Date: Fri, 29 Jan 2021 13:27:06 -0800
Subject: [PATCH 12/13] 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 @@