diff --git a/package-lock.json b/package-lock.json index d072a83..c7cd652 100644 --- a/package-lock.json +++ b/package-lock.json @@ -912,6 +912,21 @@ "to-fast-properties": "^2.0.0" } }, + "@ckeditor/ckeditor5-angular": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@ckeditor/ckeditor5-angular/-/ckeditor5-angular-1.1.0.tgz", + "integrity": "sha512-6b9NX/PhFuQKo/mR0tSpCVNGU1fhg1Y8ju5OBWxCPpIbdDZIIoqjHlhWL76EmfztteouYLACsnfrnqVQGC0utA==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "@ckeditor/ckeditor5-build-classic": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/@ckeditor/ckeditor5-build-classic/-/ckeditor5-build-classic-12.2.0.tgz", + "integrity": "sha512-En64jC5ImZoa+XLa2JrZYCKpq2iNXhdf17xgmpJoAXp+m36EqSHd6/4x0XT/pjrZSDxrcLUZxyZJlQCRtSaeHw==", + "dev": true + }, "@fortawesome/fontawesome": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome/-/fontawesome-1.1.8.tgz", @@ -6139,6 +6154,12 @@ "integrity": "sha1-ojR7o2DeGeM9D/1ZD933dVy/LmQ=", "dev": true }, + "iso-639-1": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/iso-639-1/-/iso-639-1-2.0.5.tgz", + "integrity": "sha512-2TcJ8AcsqM4AXLi92eFZX3xa7X6Eno/chq9yOR0AvSgb15Smmoh1miXyYJVWCkSmbzDimds3Ix2M4efhnOuxOg==", + "dev": true + }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", diff --git a/package.json b/package.json index 26044eb..65e54bf 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,8 @@ "@angularclass/hmr-loader": "^3.0.4", "@babel/core": "^7.4.5", "@babel/preset-env": "^7.4.5", + "@ckeditor/ckeditor5-angular": "^1.1.0", + "@ckeditor/ckeditor5-build-classic": "^12.2.0", "@fortawesome/fontawesome": "^1.1.8", "@fortawesome/fontawesome-free-brands": "^5.0.13", "@fortawesome/fontawesome-free-regular": "^5.0.13", @@ -92,6 +94,7 @@ "goby": "^1.1.2", "html-loader": "^0.5.5", "html-webpack-plugin": "^3.2.0", + "iso-639-1": "^2.0.5", "jquery": "^3.4.1", "json-loader": "^0.5.7", "mini-css-extract-plugin": "^0.7.0", diff --git a/web/app/admin/terms/new/new.component.html b/web/app/admin/terms/new/new.component.html new file mode 100644 index 0000000..6460c0d --- /dev/null +++ b/web/app/admin/terms/new/new.component.html @@ -0,0 +1,70 @@ +
+ +
+
+ +
+ + +
+
+ +
+
English version
+
+
+ + +
+
+ +
+
{{languages[code].langName}} version
+
+
+ + + +
+
+ +
+ + +
+
+ +
+ +
+
+
\ No newline at end of file diff --git a/web/app/admin/terms/new/new.component.scss b/web/app/admin/terms/new/new.component.scss new file mode 100644 index 0000000..d469ccf --- /dev/null +++ b/web/app/admin/terms/new/new.component.scss @@ -0,0 +1,8 @@ +.buttons button { + margin-right: 5px; +} + +.buttons select { + max-width: 200px; + margin-bottom: 5px; +} \ No newline at end of file diff --git a/web/app/admin/terms/new/new.component.ts b/web/app/admin/terms/new/new.component.ts new file mode 100644 index 0000000..2ffd22e --- /dev/null +++ b/web/app/admin/terms/new/new.component.ts @@ -0,0 +1,95 @@ +import { Component, OnInit } from "@angular/core"; +import { ToasterService } from "angular2-toaster"; +import { AdminTermsApiService } from "../../../shared/services/admin/admin-terms-api.service"; +import { ActivatedRoute, Router } from "@angular/router"; +import * as ClassicEditor from '@ckeditor/ckeditor5-build-classic'; +import ISO6391 from "iso-639-1"; + +@Component({ + templateUrl: "./new.component.html", + styleUrls: ["./new.component.scss"], +}) +export class AdminNewTermsComponent implements OnInit { + + public Editor = ClassicEditor; + + public isLoading = true; + public isUpdating = false; + public shortcode: string; + public takenShortcodes: string[]; + public chosenLanguage: string = ISO6391.getAllCodes()[0]; + public languages: { + [languageCode: string]: { + name: string, + text: string, + langName: string, + url: string, + isExternal: boolean, + externalUrl: string, + } + } = { + "en": { + name: "", + text: "", + langName: "English", + url: "", // TODO: Calculate + isExternal: false, + externalUrl: "", + }, + }; + + public get otherLanguageCodes(): string[] { + return Object.keys(this.languages).filter(c => c !== "en"); + } + + public get availableLanguages(): { name: string, code: string }[] { + return ISO6391.getAllCodes() + .filter(c => !this.otherLanguageCodes.includes(c)) + .map(c => { + return {code: c, name: ISO6391.getName(c)}; + }); + } + + constructor(private adminTerms: AdminTermsApiService, + private toaster: ToasterService, + private router: Router, + private activatedRoute: ActivatedRoute) { + } + + public ngOnInit() { + this.adminTerms.getAllPolicies().then(policies => { + this.takenShortcodes = policies.map(p => p.shortcode); + this.isLoading = false; + }).catch(err => { + console.error(err); + this.toaster.pop("error", "Failed to load policies"); + }); + } + + public onNameKeyUp() { + const startShortcode = this.languages['en'].name.toLowerCase().replace(/[^a-z0-9]/gi, '_'); + let shortcode = startShortcode; + let i = 0; + while (this.takenShortcodes.includes(shortcode)) { + shortcode = `${startShortcode}_${++i}`; + } + this.shortcode = shortcode; + } + + public create() { + this.router.navigate([".."], {relativeTo: this.activatedRoute}); + } + + public addLanguage() { + this.languages[this.chosenLanguage] = { + name: "", + text: "", + url: "", // TODO: Calculate + isExternal: false, + externalUrl: "", + langName: ISO6391.getName(this.chosenLanguage), + }; + this.chosenLanguage = this.availableLanguages[0].code; + } + +} diff --git a/web/app/admin/terms/terms.component.html b/web/app/admin/terms/terms.component.html index e2dd1e3..eb841c7 100644 --- a/web/app/admin/terms/terms.component.html +++ b/web/app/admin/terms/terms.component.html @@ -15,7 +15,7 @@ Policy Name - Published Version + Version Actions @@ -35,6 +35,9 @@ + \ No newline at end of file diff --git a/web/app/admin/terms/terms.component.ts b/web/app/admin/terms/terms.component.ts index bbd312c..ef16e92 100644 --- a/web/app/admin/terms/terms.component.ts +++ b/web/app/admin/terms/terms.component.ts @@ -2,6 +2,7 @@ import { Component, OnInit } from "@angular/core"; import { ToasterService } from "angular2-toaster"; import { FE_TermsEditable } from "../../shared/models/terms"; import { AdminTermsApiService } from "../../shared/services/admin/admin-terms-api.service"; +import { ActivatedRoute, Router } from "@angular/router"; @Component({ templateUrl: "./terms.component.html", @@ -13,12 +14,17 @@ export class AdminTermsComponent implements OnInit { public policies: FE_TermsEditable[]; constructor(private adminTerms: AdminTermsApiService, - private toaster: ToasterService) { + private toaster: ToasterService, + private router: Router, + private activatedRoute: ActivatedRoute) { } public ngOnInit() { this.adminTerms.getAllPolicies().then(policies => { - this.policies = policies; + this.policies = [ + ...policies.filter(p => p.version === "draft"), + ...policies.filter(p => p.version !== "draft"), + ]; this.isLoading = false; }).catch(err => { console.error(err); @@ -26,4 +32,8 @@ export class AdminTermsComponent implements OnInit { }); } + public createPolicy() { + this.router.navigate(["new"], {relativeTo: this.activatedRoute}); + } + } diff --git a/web/app/app.module.ts b/web/app/app.module.ts index 7c2aa81..cb301f9 100644 --- a/web/app/app.module.ts +++ b/web/app/app.module.ts @@ -114,6 +114,8 @@ import { ReauthExampleWidgetWrapperComponent } from "./widget-wrappers/reauth-ex import { ManagerTestWidgetWrapperComponent } from "./widget-wrappers/manager-test/manager-test.component"; import { AdminTermsApiService } from "./shared/services/admin/admin-terms-api.service"; import { AdminTermsComponent } from "./admin/terms/terms.component"; +import { AdminNewTermsComponent } from "./admin/terms/new/new.component"; +import { CKEditorModule } from "@ckeditor/ckeditor5-angular"; @NgModule({ imports: [ @@ -128,6 +130,7 @@ import { AdminTermsComponent } from "./admin/terms/terms.component"; ModalModule.forRoot(), BootstrapModalModule, BreadcrumbsModule, + CKEditorModule, ], declarations: [ AppComponent, @@ -207,6 +210,7 @@ import { AdminTermsComponent } from "./admin/terms/terms.component"; ReauthExampleWidgetWrapperComponent, ManagerTestWidgetWrapperComponent, AdminTermsComponent, + AdminNewTermsComponent, // Vendor ], diff --git a/web/app/app.routing.ts b/web/app/app.routing.ts index 8fa3bea..4d629f7 100644 --- a/web/app/app.routing.ts +++ b/web/app/app.routing.ts @@ -45,6 +45,7 @@ import { SlackBridgeConfigComponent } from "./configs/bridge/slack/slack.bridge. import { ReauthExampleWidgetWrapperComponent } from "./widget-wrappers/reauth-example/reauth-example.component"; import { ManagerTestWidgetWrapperComponent } from "./widget-wrappers/manager-test/manager-test.component"; import { AdminTermsComponent } from "./admin/terms/terms.component"; +import { AdminNewTermsComponent } from "./admin/terms/new/new.component"; const routes: Routes = [ {path: "", component: HomeComponent}, @@ -155,6 +156,11 @@ const routes: Routes = [ path: "", component: AdminTermsComponent, }, + { + path: "new", + component: AdminNewTermsComponent, + data: {breadcrumb: "New policy", name: "New policy"}, + }, ], }, ], diff --git a/web/app/elements/ibox/ibox.component.html b/web/app/elements/ibox/ibox.component.html index 990e203..ce82a88 100644 --- a/web/app/elements/ibox/ibox.component.html +++ b/web/app/elements/ibox/ibox.component.html @@ -1,6 +1,7 @@
+ [ngClass]="[isCollapsible ? 'ibox-title-collapsible' : '']" + *ngIf="hasTitle">
{{ boxTitle }}
diff --git a/web/app/elements/ibox/ibox.component.ts b/web/app/elements/ibox/ibox.component.ts index c705eb4..a16f0ba 100644 --- a/web/app/elements/ibox/ibox.component.ts +++ b/web/app/elements/ibox/ibox.component.ts @@ -11,6 +11,7 @@ export class IboxComponent implements OnInit { @Input() boxTitle: string; @Input() isCollapsible: boolean; @Input() defaultCollapsed: boolean; + @Input() hasTitle = true; public collapsed = false; diff --git a/web/app/shared/SessionStorage.ts b/web/app/shared/SessionStorage.ts index e03477d..ce77dcf 100644 --- a/web/app/shared/SessionStorage.ts +++ b/web/app/shared/SessionStorage.ts @@ -2,7 +2,21 @@ import { FE_Integration } from "./models/integration"; export class SessionStorage { - public static scalarToken: string; + private static _scalarToken: string; + + public static get scalarToken(): string { + if (this._scalarToken) return this._scalarToken; + this.scalarToken = localStorage.getItem("dimension_scalar_token"); + return this._scalarToken; + } + + public static set scalarToken(val: string) { + this._scalarToken = val; + if (val) { + localStorage.setItem("dimension_scalar_token", val); + } + } + public static userId: string; public static roomId: string; public static isAdmin: boolean;