From 90c6b6785bfa2ebed1c07519ed4b3915c65b7ad6 Mon Sep 17 00:00:00 2001
From: jojohoch <joachim.hoch@iqb.hu-berlin.de>
Date: Tue, 25 Jan 2022 13:42:13 +0100
Subject: [PATCH] [editor] Implement ImportModuleVersion to handle backwards
 compatibility

ImportModuleVersion stores the unitDefinitionType of the loaded unit
definition. During the initialization of the unit, the individual
elements can react to the stored value.
---
 docs/release-notes-player.txt                 |  8 +--
 .../common/classes/importModuleVersion.ts     | 62 +++++++++++++++++++
 projects/common/models/unit.ts                |  3 +
 .../ui-elements/dropdown/dropdown-element.ts  |  6 +-
 .../ui-elements/likert/likert-element-row.ts  |  6 +-
 .../radio-with-images/radio-group-images.ts   |  6 +-
 .../radio/radio-button-group-element.ts       |  6 +-
 .../toggle-button/toggle-button.ts            |  6 +-
 8 files changed, 89 insertions(+), 14 deletions(-)
 create mode 100644 projects/common/classes/importModuleVersion.ts

diff --git a/docs/release-notes-player.txt b/docs/release-notes-player.txt
index ee6704784..2c674cf60 100644
--- a/docs/release-notes-player.txt
+++ b/docs/release-notes-player.txt
@@ -2,15 +2,15 @@ Player
 ======
 1.16.0
 - Start the valid value range for DropDown, Likert, RadioGroup, RadioGroupImages and ToggleButton at 1
-  Attention! This may cause incompatibility with tasks created with Editor < 1.22.0 or saved with Player < 1.16.0.
-  Preset and saved values for these elements will not be displayed correctly.
-- Improve the centering of the layout
+  Attention! This can lead to incompatibilities when reloading units that were answered in IQB - TestCenter
+  with AspectPlayer < 1.16.0. Already saved results are displayed incorrectly for these elements.
+- Improve centering of layout
 - Disable logs to console in production
 - Enable autostart of audios/videos when reloading the unit
 - Mark text without selecting first
 - Show marking options as popup when text is selected
 - Implement virtual keyboards for 'place value', 'numbers and basic operators', 'square, dash and dot'
-- Show virtual keyboard optionally on the right side of the screen
+- Show virtual keyboard optionally on the right side of the view
 
 1.15.0
 - Fix restoring of toggle buttons
diff --git a/projects/common/classes/importModuleVersion.ts b/projects/common/classes/importModuleVersion.ts
new file mode 100644
index 000000000..e5da40381
--- /dev/null
+++ b/projects/common/classes/importModuleVersion.ts
@@ -0,0 +1,62 @@
+export class ImportModuleVersion {
+  private static unitLoaded: boolean;
+  private static version: string;
+
+  static setVersion(importedModuleVersion: string): void {
+    ImportModuleVersion.unitLoaded = false;
+    ImportModuleVersion.version = importedModuleVersion;
+  }
+
+  static setUnitLoaded(): void {
+    ImportModuleVersion.unitLoaded = true;
+  }
+
+  static isUnitLoaded(): boolean {
+    return ImportModuleVersion.unitLoaded;
+  }
+
+  static getVersion(): { fullName: string, type: string, version: number[] } {
+    return {
+      fullName: ImportModuleVersion.version,
+      type: ImportModuleVersion.getVersionType(),
+      version: ImportModuleVersion.getVersionNumbers()
+    };
+  }
+
+  private static getVersionType(): string {
+    if (ImportModuleVersion.version && ImportModuleVersion.version.length) {
+      const importedModuleVersionArray = ImportModuleVersion.version.split('@');
+      if (importedModuleVersionArray.length === 2) {
+        return importedModuleVersionArray[0];
+      }
+    }
+    return 'none';
+  }
+
+  private static getVersionNumbers(): number[] {
+    if (ImportModuleVersion.version && ImportModuleVersion.version.length) {
+      const importedModuleVersionArray = ImportModuleVersion.version.split('@');
+      if (importedModuleVersionArray.length === 2) {
+        const versionArray = importedModuleVersionArray[1].split('.');
+        if (versionArray.length === 3) {
+          return versionArray.map(number => Number(number));
+        }
+      }
+    }
+    return [0, 0, 0];
+  }
+
+  static verifyVersion(): boolean {
+    if (!ImportModuleVersion.getVersion().fullName) {
+      return false;
+    }
+    if (ImportModuleVersion.getVersionType() !== 'iqb-aspect-definition') {
+      return false;
+    }
+    const numbers = ImportModuleVersion.getVersionNumbers();
+    if (numbers[0] < 1) {
+      return false;
+    }
+    return numbers[1] >= 1;
+  }
+}
diff --git a/projects/common/models/unit.ts b/projects/common/models/unit.ts
index cba6a3b6e..fc6b21866 100644
--- a/projects/common/models/unit.ts
+++ b/projects/common/models/unit.ts
@@ -1,5 +1,6 @@
 import { Page } from './page';
 import { moveArrayItem } from '../util/array';
+import { ImportModuleVersion } from '../classes/importModuleVersion';
 
 export const EXPORTED_MODULE_VERSION = 'iqb-aspect-definition@1.1.0';
 
@@ -9,9 +10,11 @@ export class Unit {
 
   constructor(serializedUnit?: Unit) {
     if (serializedUnit && serializedUnit.pages.length > 0) {
+      ImportModuleVersion.setVersion(serializedUnit.unitDefinitionType || '');
       serializedUnit?.pages.forEach((page: Page) => {
         this.pages.push(new Page(page));
       });
+      ImportModuleVersion.setUnitLoaded();
     } else {
       this.pages.push(new Page());
     }
diff --git a/projects/common/ui-elements/dropdown/dropdown-element.ts b/projects/common/ui-elements/dropdown/dropdown-element.ts
index e03817807..d6ae6f2ea 100644
--- a/projects/common/ui-elements/dropdown/dropdown-element.ts
+++ b/projects/common/ui-elements/dropdown/dropdown-element.ts
@@ -8,6 +8,7 @@ import {
   UIElement
 } from '../../models/uI-element';
 import { initFontElement, initPositionedElement, initSurfaceElement } from '../../util/unit-interface-initializer';
+import { ImportModuleVersion } from '../../classes/importModuleVersion';
 
 export class DropdownElement extends InputElement implements PositionedElement, FontElement, SurfaceElement {
   options: string[] = [];
@@ -30,8 +31,9 @@ export class DropdownElement extends InputElement implements PositionedElement,
   }
 
   handleBackwardsCompatibility(serializedElement: Partial<UIElement>): void {
-    if (serializedElement.value === 0 && serializedElement.options && serializedElement.options.length) {
-      this.value = 1;
+    if ((serializedElement.value || serializedElement.value === 0) &&
+      !ImportModuleVersion.isUnitLoaded() && !ImportModuleVersion.verifyVersion()) {
+      this.value = Number(this.value) + 1;
     }
   }
 }
diff --git a/projects/common/ui-elements/likert/likert-element-row.ts b/projects/common/ui-elements/likert/likert-element-row.ts
index 5d0203656..b49b2bf0d 100644
--- a/projects/common/ui-elements/likert/likert-element-row.ts
+++ b/projects/common/ui-elements/likert/likert-element-row.ts
@@ -1,4 +1,5 @@
 import { InputElement, LikertRow, UIElement } from '../../models/uI-element';
+import { ImportModuleVersion } from '../../classes/importModuleVersion';
 
 export class LikertElementRow extends InputElement implements LikertRow {
   text: string = '';
@@ -12,8 +13,9 @@ export class LikertElementRow extends InputElement implements LikertRow {
   }
 
   handleBackwardsCompatibility(serializedElement: Partial<UIElement>): void {
-    if (serializedElement.value === 0 && serializedElement.columnCount) {
-      this.value = 1;
+    if ((serializedElement.value || serializedElement.value === 0) &&
+      !ImportModuleVersion.isUnitLoaded() && !ImportModuleVersion.verifyVersion()) {
+      this.value = Number(this.value) + 1;
     }
   }
 }
diff --git a/projects/common/ui-elements/radio-with-images/radio-group-images.ts b/projects/common/ui-elements/radio-with-images/radio-group-images.ts
index 6c9987158..1a321d267 100644
--- a/projects/common/ui-elements/radio-with-images/radio-group-images.ts
+++ b/projects/common/ui-elements/radio-with-images/radio-group-images.ts
@@ -8,6 +8,7 @@ import {
   UIElement
 } from '../../models/uI-element';
 import { initFontElement, initPositionedElement, initSurfaceElement } from '../../util/unit-interface-initializer';
+import { ImportModuleVersion } from '../../classes/importModuleVersion';
 
 export class RadioGroupImagesElement extends InputElement implements PositionedElement, FontElement, SurfaceElement {
   columns: LikertColumn[] = []; // TODO
@@ -32,8 +33,9 @@ export class RadioGroupImagesElement extends InputElement implements PositionedE
   }
 
   handleBackwardsCompatibility(serializedElement: Partial<UIElement>): void {
-    if (serializedElement.value === 0 && serializedElement.columns && serializedElement.columns.length) {
-      this.value = 1;
+    if ((serializedElement.value || serializedElement.value === 0) &&
+      !ImportModuleVersion.isUnitLoaded() && !ImportModuleVersion.verifyVersion()) {
+      this.value = Number(this.value) + 1;
     }
   }
 }
diff --git a/projects/common/ui-elements/radio/radio-button-group-element.ts b/projects/common/ui-elements/radio/radio-button-group-element.ts
index c7f07e9fa..816b65a66 100644
--- a/projects/common/ui-elements/radio/radio-button-group-element.ts
+++ b/projects/common/ui-elements/radio/radio-button-group-element.ts
@@ -8,6 +8,7 @@ import {
   UIElement
 } from '../../models/uI-element';
 import { initFontElement, initPositionedElement, initSurfaceElement } from '../../util/unit-interface-initializer';
+import { ImportModuleVersion } from '../../classes/importModuleVersion';
 
 export class RadioButtonGroupElement extends InputElement implements PositionedElement, FontElement, SurfaceElement {
   options: string[] = [];
@@ -41,8 +42,9 @@ export class RadioButtonGroupElement extends InputElement implements PositionedE
   }
 
   handleBackwardsCompatibility(serializedElement: Partial<UIElement>): void {
-    if (serializedElement.value === 0 && serializedElement.options && serializedElement.options.length) {
-      this.value = 1;
+    if ((serializedElement.value || serializedElement.value === 0) &&
+      !ImportModuleVersion.isUnitLoaded() && !ImportModuleVersion.verifyVersion()) {
+      this.value = Number(this.value) + 1;
     }
   }
 }
diff --git a/projects/common/ui-elements/toggle-button/toggle-button.ts b/projects/common/ui-elements/toggle-button/toggle-button.ts
index e86578c1b..f582bbf47 100644
--- a/projects/common/ui-elements/toggle-button/toggle-button.ts
+++ b/projects/common/ui-elements/toggle-button/toggle-button.ts
@@ -6,6 +6,7 @@ import {
   FontProperties, SurfaceProperties
 } from '../../models/uI-element';
 import { initFontElement, initSurfaceElement } from '../../util/unit-interface-initializer';
+import { ImportModuleVersion } from '../../classes/importModuleVersion';
 
 export class ToggleButtonElement extends InputElement implements FontElement, SurfaceElement {
   options: string[] = ['A', 'B'];
@@ -33,8 +34,9 @@ export class ToggleButtonElement extends InputElement implements FontElement, Su
   }
 
   handleBackwardsCompatibility(serializedElement: Partial<UIElement>): void {
-    if (serializedElement.value === 0 && serializedElement.options && serializedElement.options.length) {
-      this.value = 1;
+    if ((serializedElement.value || serializedElement.value === 0) &&
+      !ImportModuleVersion.isUnitLoaded() && !ImportModuleVersion.verifyVersion()) {
+      this.value = Number(this.value) + 1;
     }
   }
 }
-- 
GitLab