From 542fca6d7612d8065b94e071f03652f82ee1478b Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Sun, 27 Sep 2020 11:47:23 +0200 Subject: [PATCH] Sidebar plugin. (#581) * Sidebar plugin. --- backend/i18n/frontend_en.json | 5 + backend/i18n/frontend_it.json | 5 + backend/i18n/frontend_nl.json | 5 + backend/i18n/source/frontend_en.json | 5 + .../Schemas/SchemaProperties.cs | 4 + .../Schemas/Models/SchemaPropertiesDto.cs | 10 + .../Schemas/Models/UpdateSchemaDto.cs | 10 + .../wwwroot/scripts/context-editor.html | 28 - ...bined-editor.html => editor-combined.html} | 4 +- .../wwwroot/scripts/editor-context.html | 47 ++ .../wwwroot/scripts/editor-json-schema.html | 3 + .../{simple-log.html => editor-log.html} | 11 +- .../src/Squidex/wwwroot/scripts/editor-sdk.js | 146 ++++- ...{simple-editor.html => editor-simple.html} | 4 +- .../wwwroot/scripts/sidebar-context.html | 49 ++ .../wwwroot/scripts/sidebar-search.html | 118 ++++ frontend/app/features/content/declarations.ts | 1 + frontend/app/features/content/module.ts | 15 +- .../comments/comments-page.component.html | 10 +- .../pages/content/content-field.component.ts | 11 +- .../pages/content/content-page.component.html | 4 + .../pages/content/content-page.component.ts | 11 +- .../contents/contents-page.component.html | 4 + .../pages/contents/contents-page.component.ts | 4 +- .../pages/sidebar/sidebar-page.component.html | 15 + .../pages/sidebar/sidebar-page.component.scss | 5 + .../pages/sidebar/sidebar-page.component.ts | 113 ++++ .../pages/rules/rules-page.component.html | 8 +- .../common/schema-edit-form.component.html | 20 + .../fields/forms/field-form-ui.component.html | 2 +- .../forms/editors/iframe-editor.component.ts | 14 +- .../comments/comments.component.html | 49 +- .../guards/content-must-exist.guard.spec.ts | 2 +- .../shared/services/schemas.service.spec.ts | 26 +- .../app/shared/services/schemas.service.ts | 19 +- frontend/app/shared/state/schemas.forms.ts | 2 + frontend/app/theme/_panels.scss | 2 + .../app/theme/icomoon/demo-files/demo.css | 2 +- frontend/app/theme/icomoon/demo.html | 556 +++++++++--------- frontend/app/theme/icomoon/fonts/icomoon.eot | Bin 31928 -> 32412 bytes frontend/app/theme/icomoon/fonts/icomoon.svg | 1 + frontend/app/theme/icomoon/fonts/icomoon.ttf | Bin 31764 -> 32248 bytes frontend/app/theme/icomoon/fonts/icomoon.woff | Bin 31840 -> 32324 bytes frontend/app/theme/icomoon/selection.json | 2 +- frontend/app/theme/icomoon/style.css | 161 ++--- 45 files changed, 1058 insertions(+), 455 deletions(-) delete mode 100644 backend/src/Squidex/wwwroot/scripts/context-editor.html rename backend/src/Squidex/wwwroot/scripts/{combined-editor.html => editor-combined.html} (88%) create mode 100644 backend/src/Squidex/wwwroot/scripts/editor-context.html rename backend/src/Squidex/wwwroot/scripts/{simple-log.html => editor-log.html} (82%) rename backend/src/Squidex/wwwroot/scripts/{simple-editor.html => editor-simple.html} (91%) create mode 100644 backend/src/Squidex/wwwroot/scripts/sidebar-context.html create mode 100644 backend/src/Squidex/wwwroot/scripts/sidebar-search.html create mode 100644 frontend/app/features/content/pages/sidebar/sidebar-page.component.html create mode 100644 frontend/app/features/content/pages/sidebar/sidebar-page.component.scss create mode 100644 frontend/app/features/content/pages/sidebar/sidebar-page.component.ts diff --git a/backend/i18n/frontend_en.json b/backend/i18n/frontend_en.json index d923512e9..81e1a1628 100644 --- a/backend/i18n/frontend_en.json +++ b/backend/i18n/frontend_en.json @@ -301,6 +301,7 @@ "common.searchResults": "Search Results", "common.separateByLine": "Separate by line", "common.settings": "Settings", + "common.sidebar": "Sidebar Extension", "common.sidebarTour": "The sidebar navigation contains useful context specific links. Here you can view the history how this schema has changed over time.", "common.slug": "Slug", "common.stars.max": "Must not have more more than 15 stars", @@ -622,6 +623,10 @@ "schemas.addNestedField": "Add Nested Field", "schemas.changeCategoryFailed": "Failed to change category. Please reload.", "schemas.clone": "Clone Schema", + "schemas.contentSidebarUrl": "Content Sidebar Extension", + "schemas.contentSidebarUrlHint": "URL to the plugin for the sidebar in the details view.", + "schemas.contentsSidebarUrl": "Contents Sidebar Extension", + "schemas.contentsSidebarUrlHint": "URL to the plugin for the sidebar in the list view.", "schemas.contextMenuTour": "Open the context menu to delete the schema or to create some scripts for content changes.", "schemas.create": "Create Schema", "schemas.createCategory": "Create new category...", diff --git a/backend/i18n/frontend_it.json b/backend/i18n/frontend_it.json index 0fcf9432b..f5f87d70d 100644 --- a/backend/i18n/frontend_it.json +++ b/backend/i18n/frontend_it.json @@ -301,6 +301,7 @@ "common.searchResults": "Risultati di ricerca", "common.separateByLine": "Separato dalla linea", "common.settings": "Impostazioni", + "common.sidebar": "Sidebar Extension", "common.sidebarTour": "La barra di navigazione laterale contiene specifici utili collegamenti per il contesto. Qui puoi visualizzare la cronologia dei cambiamenti di questo schema.", "common.slug": "Slug", "common.stars.max": "Non deve avere più di 15 stelle", @@ -622,6 +623,10 @@ "schemas.addNestedField": "Aggiungi un campo annidato", "schemas.changeCategoryFailed": "Non è stato possibile cambiare la categoria. Per favore ricarica.", "schemas.clone": "Clona lo Schema", + "schemas.contentSidebarUrl": "Content Sidebar Extension", + "schemas.contentSidebarUrlHint": "URL to the plugin for the sidebar in the details view.", + "schemas.contentsSidebarUrl": "Contents Sidebar Extension", + "schemas.contentsSidebarUrlHint": "URL to the plugin for the sidebar in the list view.", "schemas.contextMenuTour": "Apri il menu per cancellare lo schema o per inserire alcuni script che modificano il contenuto.", "schemas.create": "Crea uno Schema", "schemas.createCategory": "Crea una nuova categoria...", diff --git a/backend/i18n/frontend_nl.json b/backend/i18n/frontend_nl.json index a537dbf5e..5c67eb6ab 100644 --- a/backend/i18n/frontend_nl.json +++ b/backend/i18n/frontend_nl.json @@ -301,6 +301,7 @@ "common.searchResults": "Zoekresultaten", "common.separateByLine": "Scheiden op regel", "common.settings": "Instellingen", + "common.sidebar": "Sidebar Extension", "common.sidebarTour": "De zijbalknavigatie bevat nuttige contextspecifieke links. Hier kun je de geschiedenis bekijken hoe dit schema in de loop van de tijd is veranderd.", "common.slug": "Slug", "common.stars.max": "Mag niet meer dan 15 sterren hebben", @@ -622,6 +623,10 @@ "schemas.addNestedField": "Voeg genest veld toe", "schemas.changeCategoryFailed": "Kan categorie niet wijzigen. Laad opnieuw.", "schemas.clone": "Clone Schema", + "schemas.contentSidebarUrl": "Content Sidebar Extension", + "schemas.contentSidebarUrlHint": "URL to the plugin for the sidebar in the details view.", + "schemas.contentsSidebarUrl": "Contents Sidebar Extension", + "schemas.contentsSidebarUrlHint": "URL to the plugin for the sidebar in the list view.", "schemas.contextMenuTour": "Open het contextmenu om het schema te verwijderen of om scripts te maken voor wijzigingen in de inhoud.", "schemas.create": "Schema maken", "schemas.createCategory": "Nieuwe categorie maken ...", diff --git a/backend/i18n/source/frontend_en.json b/backend/i18n/source/frontend_en.json index d923512e9..81e1a1628 100644 --- a/backend/i18n/source/frontend_en.json +++ b/backend/i18n/source/frontend_en.json @@ -301,6 +301,7 @@ "common.searchResults": "Search Results", "common.separateByLine": "Separate by line", "common.settings": "Settings", + "common.sidebar": "Sidebar Extension", "common.sidebarTour": "The sidebar navigation contains useful context specific links. Here you can view the history how this schema has changed over time.", "common.slug": "Slug", "common.stars.max": "Must not have more more than 15 stars", @@ -622,6 +623,10 @@ "schemas.addNestedField": "Add Nested Field", "schemas.changeCategoryFailed": "Failed to change category. Please reload.", "schemas.clone": "Clone Schema", + "schemas.contentSidebarUrl": "Content Sidebar Extension", + "schemas.contentSidebarUrlHint": "URL to the plugin for the sidebar in the details view.", + "schemas.contentsSidebarUrl": "Contents Sidebar Extension", + "schemas.contentsSidebarUrlHint": "URL to the plugin for the sidebar in the list view.", "schemas.contextMenuTour": "Open the context menu to delete the schema or to create some scripts for content changes.", "schemas.create": "Create Schema", "schemas.createCategory": "Create new category...", diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaProperties.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaProperties.cs index bb2e8a27c..fa06a7f61 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaProperties.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaProperties.cs @@ -15,6 +15,10 @@ namespace Squidex.Domain.Apps.Core.Schemas { public ReadOnlyCollection? Tags { get; set; } + public string? ContentsSidebarUrl { get; set; } + + public string? ContentSidebarUrl { get; set; } + public bool DeepEquals(SchemaProperties properties) { return SimpleEquals.IsEquals(this, properties); diff --git a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaPropertiesDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaPropertiesDto.cs index cc629a5e5..c8630b2df 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaPropertiesDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaPropertiesDto.cs @@ -24,6 +24,16 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models [LocalizedStringLength(1000)] public string? Hints { get; set; } + /// + /// The url to a the sidebar plugin for content lists. + /// + public string? ContentsSidebarUrl { get; set; } + + /// + /// The url to a the sidebar plugin for content items. + /// + public string? ContentSidebarUrl { get; set; } + /// /// Tags for automation processes. /// diff --git a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpdateSchemaDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpdateSchemaDto.cs index 576fc669f..fec58f16c 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpdateSchemaDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpdateSchemaDto.cs @@ -27,6 +27,16 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models [LocalizedStringLength(1000)] public string? Hints { get; set; } + /// + /// The url to a the sidebar plugin for content lists. + /// + public string? ContentsSidebarUrl { get; set; } + + /// + /// The url to a the sidebar plugin for content items. + /// + public string? ContentSidebarUrl { get; set; } + /// /// Tags for automation processes. /// diff --git a/backend/src/Squidex/wwwroot/scripts/context-editor.html b/backend/src/Squidex/wwwroot/scripts/context-editor.html deleted file mode 100644 index e44344ae8..000000000 --- a/backend/src/Squidex/wwwroot/scripts/context-editor.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/backend/src/Squidex/wwwroot/scripts/combined-editor.html b/backend/src/Squidex/wwwroot/scripts/editor-combined.html similarity index 88% rename from backend/src/Squidex/wwwroot/scripts/combined-editor.html rename to backend/src/Squidex/wwwroot/scripts/editor-combined.html index 556b6ebb8..5d2abe715 100644 --- a/backend/src/Squidex/wwwroot/scripts/combined-editor.html +++ b/backend/src/Squidex/wwwroot/scripts/editor-combined.html @@ -14,7 +14,9 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/backend/src/Squidex/wwwroot/scripts/editor-json-schema.html b/backend/src/Squidex/wwwroot/scripts/editor-json-schema.html index 281af4e8b..2054ad8cd 100644 --- a/backend/src/Squidex/wwwroot/scripts/editor-json-schema.html +++ b/backend/src/Squidex/wwwroot/scripts/editor-json-schema.html @@ -29,6 +29,9 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/backend/src/Squidex/wwwroot/scripts/sidebar-search.html b/backend/src/Squidex/wwwroot/scripts/sidebar-search.html new file mode 100644 index 000000000..8ba7ff6a3 --- /dev/null +++ b/backend/src/Squidex/wwwroot/scripts/sidebar-search.html @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ + + + + \ No newline at end of file diff --git a/frontend/app/features/content/declarations.ts b/frontend/app/features/content/declarations.ts index 2245c5082..7b1c41dcf 100644 --- a/frontend/app/features/content/declarations.ts +++ b/frontend/app/features/content/declarations.ts @@ -16,6 +16,7 @@ export * from './pages/contents/contents-filters-page.component'; export * from './pages/contents/contents-page.component'; export * from './pages/contents/custom-view-editor.component'; export * from './pages/schemas/schemas-page.component'; +export * from './pages/sidebar/sidebar-page.component'; export * from './shared/content-status.component'; export * from './shared/due-time-selector.component'; export * from './shared/forms/array-editor.component'; diff --git a/frontend/app/features/content/module.ts b/frontend/app/features/content/module.ts index a16823683..4848f56f9 100644 --- a/frontend/app/features/content/module.ts +++ b/frontend/app/features/content/module.ts @@ -10,7 +10,7 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { CanDeactivateGuard, ContentMustExistGuard, LoadLanguagesGuard, SchemaMustExistPublishedGuard, SchemaMustNotBeSingletonGuard, SqxFrameworkModule, SqxSharedModule, UnsetContentGuard } from '@app/shared'; -import { ArrayEditorComponent, ArrayItemComponent, ArraySectionComponent, AssetsEditorComponent, CommentsPageComponent, ContentComponent, ContentCreatorComponent, ContentEventComponent, ContentFieldComponent, ContentHistoryPageComponent, ContentListCellDirective, ContentListFieldComponent, ContentListHeaderComponent, ContentListWidthPipe, ContentPageComponent, ContentSectionComponent, ContentSelectorComponent, ContentSelectorItemComponent, ContentsFiltersPageComponent, ContentsPageComponent, ContentStatusComponent, ContentValueComponent, ContentValueEditorComponent, CustomViewEditorComponent, DueTimeSelectorComponent, FieldEditorComponent, FieldLanguagesComponent, PreviewButtonComponent, ReferenceItemComponent, ReferencesEditorComponent, SchemasPageComponent, StockPhotoEditorComponent } from './declarations'; +import { ArrayEditorComponent, ArrayItemComponent, ArraySectionComponent, AssetsEditorComponent, CommentsPageComponent, ContentComponent, ContentCreatorComponent, ContentEventComponent, ContentFieldComponent, ContentHistoryPageComponent, ContentListCellDirective, ContentListFieldComponent, ContentListHeaderComponent, ContentListWidthPipe, ContentPageComponent, ContentSectionComponent, ContentSelectorComponent, ContentSelectorItemComponent, ContentsFiltersPageComponent, ContentsPageComponent, ContentStatusComponent, ContentValueComponent, ContentValueEditorComponent, CustomViewEditorComponent, DueTimeSelectorComponent, FieldEditorComponent, FieldLanguagesComponent, PreviewButtonComponent, ReferenceItemComponent, ReferencesEditorComponent, SchemasPageComponent, SidebarPageComponent, StockPhotoEditorComponent } from './declarations'; const routes: Routes = [ { @@ -28,12 +28,16 @@ const routes: Routes = [ { path: '', component: ContentsPageComponent, - canActivate: [SchemaMustNotBeSingletonGuard], + canActivate: [SchemaMustNotBeSingletonGuard, UnsetContentGuard], canDeactivate: [CanDeactivateGuard], children: [ { path: 'filters', component: ContentsFiltersPageComponent + }, + { + path: 'sidebar', + component: SidebarPageComponent } ] }, @@ -59,7 +63,11 @@ const routes: Routes = [ { path: 'comments', component: CommentsPageComponent - } + }, + { + path: 'sidebar', + component: SidebarPageComponent + } ] } ] @@ -105,6 +113,7 @@ const routes: Routes = [ ReferenceItemComponent, ReferencesEditorComponent, SchemasPageComponent, + SidebarPageComponent, StockPhotoEditorComponent ] }) diff --git a/frontend/app/features/content/pages/comments/comments-page.component.html b/frontend/app/features/content/pages/comments/comments-page.component.html index 5a63794b5..d3d658289 100644 --- a/frontend/app/features/content/pages/comments/comments-page.component.html +++ b/frontend/app/features/content/pages/comments/comments-page.component.html @@ -1 +1,9 @@ - \ No newline at end of file + + + {{ 'comments.title' | sqxTranslate }} + + + + + + \ No newline at end of file diff --git a/frontend/app/features/content/pages/content/content-field.component.ts b/frontend/app/features/content/pages/content/content-field.component.ts index 2857e5b69..05181c54c 100644 --- a/frontend/app/features/content/pages/content/content-field.component.ts +++ b/frontend/app/features/content/pages/content/content-field.component.ts @@ -7,8 +7,8 @@ import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; import { AppLanguageDto, AppsState, EditContentForm, FieldForm, invalid$, LocalStoreService, SchemaDto, Settings, StringFieldPropertiesDto, TranslationsService, Types, value$ } from '@app/shared'; -import { Observable } from 'rxjs'; -import { combineLatest } from 'rxjs/operators'; +import { combineLatest, Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; @Component({ selector: 'sqx-content-field', @@ -78,9 +78,10 @@ export class ContentFieldComponent implements OnChanges { if ((changes['formModel'] || changes['formModelCompare']) && this.formModelCompare) { this.isDifferent = - value$(this.formModel.form).pipe( - combineLatest(value$(this.formModelCompare!.form), - (lhs, rhs) => !Types.equals(lhs, rhs, true))); + combineLatest([ + value$(this.formModel.form), + value$(this.formModelCompare!.form) + ]).pipe(map(([lhs, rhs]) => !Types.equals(lhs, rhs, true))); } } diff --git a/frontend/app/features/content/pages/content/content-page.component.html b/frontend/app/features/content/pages/content/content-page.component.html index 03e956b27..8a36ce99f 100644 --- a/frontend/app/features/content/pages/content/content-page.component.html +++ b/frontend/app/features/content/pages/content/content-page.component.html @@ -104,6 +104,10 @@ + + + + {{ 'common.sidebarTour' | sqxTranslate }} diff --git a/frontend/app/features/content/pages/content/content-page.component.ts b/frontend/app/features/content/pages/content/content-page.component.ts index a88859e67..67f279d76 100644 --- a/frontend/app/features/content/pages/content/content-page.component.ts +++ b/frontend/app/features/content/pages/content/content-page.component.ts @@ -9,7 +9,7 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { ApiUrlConfig, AppLanguageDto, AuthService, AutoSaveKey, AutoSaveService, CanComponentDeactivate, ContentDto, ContentsState, DialogService, EditContentForm, fadeAnimation, FieldForm, FieldSection, LanguagesState, ModalModel, ResourceOwner, RootFieldDto, SchemaDetailsDto, SchemasState, TempService, valueAll$, Version } from '@app/shared'; +import { ApiUrlConfig, AppLanguageDto, AppsState, AuthService, AutoSaveKey, AutoSaveService, CanComponentDeactivate, ContentDto, ContentsState, DialogService, EditContentForm, fadeAnimation, FieldForm, FieldSection, LanguagesState, ModalModel, ResourceOwner, RootFieldDto, SchemaDetailsDto, SchemasState, TempService, valueAll$, Version } from '@app/shared'; import { Observable, of } from 'rxjs'; import { debounceTime, filter, onErrorResumeNext, tap } from 'rxjs/operators'; @@ -39,7 +39,7 @@ export class ContentPageComponent extends ResourceOwner implements CanComponentD public language: AppLanguageDto; public languages: ReadonlyArray; - constructor(apiUrl: ApiUrlConfig, authService: AuthService, + constructor(apiUrl: ApiUrlConfig, authService: AuthService, appsState: AppsState, public readonly contentsState: ContentsState, private readonly autoSaveService: AutoSaveService, private readonly dialogs: DialogService, @@ -51,7 +51,12 @@ export class ContentPageComponent extends ResourceOwner implements CanComponentD ) { super(); - this.formContext = { user: authService.user, apiUrl: apiUrl.buildUrl('api') }; + this.formContext = { + apiUrl: apiUrl.buildUrl('api'), + appId: appsState.snapshot.selectedApp!.id, + appName: appsState.snapshot.selectedApp!.name, + user: authService.user + }; } public ngOnInit() { diff --git a/frontend/app/features/content/pages/contents/contents-page.component.html b/frontend/app/features/content/pages/contents/contents-page.component.html index 8dc4ecce3..b2d0f14c8 100644 --- a/frontend/app/features/content/pages/contents/contents-page.component.html +++ b/frontend/app/features/content/pages/contents/contents-page.component.html @@ -130,6 +130,10 @@ + + + + diff --git a/frontend/app/features/content/pages/contents/contents-page.component.ts b/frontend/app/features/content/pages/contents/contents-page.component.ts index f69c41cef..256580a45 100644 --- a/frontend/app/features/content/pages/contents/contents-page.component.ts +++ b/frontend/app/features/content/pages/contents/contents-page.component.ts @@ -65,11 +65,11 @@ export class ContentsPageComponent extends ResourceOwner implements OnInit { public ngOnInit() { this.own( - combineLatest( + combineLatest([ this.schemasState.selectedSchema, this.languagesState.languages, this.contentsState.statuses - ).subscribe(([schema, languages, statuses]) => { + ]).subscribe(([schema, languages, statuses]) => { this.queryModel = queryModelFromSchema(schema, languages.map(x => x.language), statuses); })); diff --git a/frontend/app/features/content/pages/sidebar/sidebar-page.component.html b/frontend/app/features/content/pages/sidebar/sidebar-page.component.html new file mode 100644 index 000000000..f8a1476f9 --- /dev/null +++ b/frontend/app/features/content/pages/sidebar/sidebar-page.component.html @@ -0,0 +1,15 @@ + + + {{ 'common.sidebar' | sqxTranslate }} + + + +
+ +
+
+
+ + + + diff --git a/frontend/app/features/content/pages/sidebar/sidebar-page.component.scss b/frontend/app/features/content/pages/sidebar/sidebar-page.component.scss new file mode 100644 index 000000000..ae2678456 --- /dev/null +++ b/frontend/app/features/content/pages/sidebar/sidebar-page.component.scss @@ -0,0 +1,5 @@ +iframe { + background: 0; + border: 0; + overflow: hidden; +} \ No newline at end of file diff --git a/frontend/app/features/content/pages/sidebar/sidebar-page.component.ts b/frontend/app/features/content/pages/sidebar/sidebar-page.component.ts new file mode 100644 index 000000000..9e02dd1e0 --- /dev/null +++ b/frontend/app/features/content/pages/sidebar/sidebar-page.component.ts @@ -0,0 +1,113 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. + */ + +import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, Renderer2, ViewChild } from '@angular/core'; +import { Router } from '@angular/router'; +import { ApiUrlConfig, ResourceOwner, Types } from '@app/framework/internal'; +import { AppsState, AuthService, ContentsState, SchemasState } from '@app/shared'; +import { combineLatest } from 'rxjs'; + +@Component({ + selector: 'sqx-sidebar-page', + styleUrls: ['./sidebar-page.component.scss'], + templateUrl: './sidebar-page.component.html', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class SidebarPageComponent extends ResourceOwner implements AfterViewInit { + private isInitialized = false; + private context: any; + private content: any; + + @ViewChild('iframe', { static: false }) + public iframe: ElementRef; + + constructor(apiUrl: ApiUrlConfig, authService: AuthService, appsState: AppsState, + private readonly contentsState: ContentsState, + private readonly schemasState: SchemasState, + private readonly renderer: Renderer2, + private readonly router: Router + ) { + super(); + + this.context = { + apiUrl: apiUrl.buildUrl('api'), + appId: appsState.snapshot.selectedApp!.id, + appName: appsState.snapshot.selectedApp!.name, + user: authService.user + }; + } + + public ngAfterViewInit() { + this.own( + combineLatest([ + this.schemasState.selectedSchema, + this.contentsState.selectedContent + ]).subscribe(([schema, content]) => { + const url = + content ? + schema.properties.contentSidebarUrl : + schema.properties.contentsSidebarUrl; + + this.context['schemaName'] = schema.name; + this.context['schemaId'] = schema.id; + + this.iframe.nativeElement.src = url || ''; + })); + + this.own( + this.contentsState.selectedContent + .subscribe(content => { + this.content = content; + + this.sendContent(); + })); + + this.own( + this.renderer.listen('window', 'message', (event: MessageEvent) => { + if (event.source === this.iframe.nativeElement.contentWindow) { + const { type } = event.data; + + if (type === 'started') { + this.isInitialized = true; + + this.sendInit(); + this.sendContent(); + } else if (type === 'resize') { + const { height } = event.data; + + this.iframe.nativeElement.height = height + 'px'; + } else if (type === 'navigate') { + const { url } = event.data; + + this.router.navigateByUrl(url); + } + } + })); + } + + private sendInit() { + this.sendMessage('init', { context: this.context }); + } + + private sendContent() { + this.sendMessage('contentChanged', { content: this.content }); + } + + private sendMessage(type: string, payload: any) { + if (!this.iframe) { + return; + } + + const iframe = this.iframe.nativeElement; + + if (this.isInitialized && iframe.contentWindow && Types.isFunction(iframe.contentWindow.postMessage)) { + const message = { type, ...payload }; + + iframe.contentWindow.postMessage(message, '*'); + } + } +} \ No newline at end of file diff --git a/frontend/app/features/rules/pages/rules/rules-page.component.html b/frontend/app/features/rules/pages/rules/rules-page.component.html index 6a86d80f2..d7a77c019 100644 --- a/frontend/app/features/rules/pages/rules/rules-page.component.html +++ b/frontend/app/features/rules/pages/rules/rules-page.component.html @@ -71,11 +71,9 @@ +
+ + + + + + + {{ 'schemas.contentsSidebarUrl' | sqxTranslate }} +
+ +
+ + + + + + + {{ 'schemas.contentSidebarUrlHint' | sqxTranslate }} +
+
diff --git a/frontend/app/features/schemas/pages/schema/fields/forms/field-form-ui.component.html b/frontend/app/features/schemas/pages/schema/fields/forms/field-form-ui.component.html index e8e41106b..5b6edff4c 100644 --- a/frontend/app/features/schemas/pages/schema/fields/forms/field-form-ui.component.html +++ b/frontend/app/features/schemas/pages/schema/fields/forms/field-form-ui.component.html @@ -3,7 +3,7 @@
- + {{ 'schemas.field.editorUrlHint' | sqxTranslate }} diff --git a/frontend/app/framework/angular/forms/editors/iframe-editor.component.ts b/frontend/app/framework/angular/forms/editors/iframe-editor.component.ts index 510e04b1d..f0c9e7ea2 100644 --- a/frontend/app/framework/angular/forms/editors/iframe-editor.component.ts +++ b/frontend/app/framework/angular/forms/editors/iframe-editor.component.ts @@ -7,6 +7,7 @@ import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, forwardRef, Input, OnChanges, Renderer2, SimpleChanges, ViewChild } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; +import { Router } from '@angular/router'; import { StatefulControlComponent, Types } from '@app/framework/internal'; export const SQX_IFRAME_EDITOR_CONTROL_VALUE_ACCESSOR: any = { @@ -40,7 +41,8 @@ export class IFrameEditorComponent extends StatefulControlComponent im public url: string; constructor(changeDetector: ChangeDetectorRef, - private readonly renderer: Renderer2 + private readonly renderer: Renderer2, + private readonly router: Router ) { super(changeDetector, {}); } @@ -72,7 +74,7 @@ export class IFrameEditorComponent extends StatefulControlComponent im if (type === 'started') { this.isInitialized = true; - this.sendMessage('init', { context: this.context || {} }); + this.sendInit(); this.sendFormValue(); this.sendDisabled(); this.sendValue(); @@ -80,6 +82,10 @@ export class IFrameEditorComponent extends StatefulControlComponent im const { height } = event.data; this.iframe.nativeElement.height = height + 'px'; + } else if (type === 'navigate') { + const { url } = event.data; + + this.router.navigateByUrl(url); } else if (type === 'valueChanged') { const { value } = event.data; @@ -107,6 +113,10 @@ export class IFrameEditorComponent extends StatefulControlComponent im this.sendDisabled(); } + private sendInit() { + this.sendMessage('init', { context: this.context || {} }); + } + private sendValue() { this.sendMessage('valueChanged', { value: this.value }); } diff --git a/frontend/app/shared/components/comments/comments.component.html b/frontend/app/shared/components/comments/comments.component.html index 7599d054c..6dd0ba859 100644 --- a/frontend/app/shared/components/comments/comments.component.html +++ b/frontend/app/shared/components/comments/comments.component.html @@ -1,27 +1,28 @@ - - - {{ 'comments.title' | sqxTranslate }} - - - - - -
-
- - -
-
- - -
-
-
- + +
+
+ + +
+
+ + +
diff --git a/frontend/app/shared/guards/content-must-exist.guard.spec.ts b/frontend/app/shared/guards/content-must-exist.guard.spec.ts index 9dd51e1d3..a7e99b0dc 100644 --- a/frontend/app/shared/guards/content-must-exist.guard.spec.ts +++ b/frontend/app/shared/guards/content-must-exist.guard.spec.ts @@ -18,8 +18,8 @@ describe('ContentMustExistGuard', () => { } }; - let contentsState: IMock; let router: IMock; + let contentsState: IMock; let contentGuard: ContentMustExistGuard; beforeEach(() => { diff --git a/frontend/app/shared/services/schemas.service.spec.ts b/frontend/app/shared/services/schemas.service.spec.ts index 0ac097ec3..b2084aef5 100644 --- a/frontend/app/shared/services/schemas.service.spec.ts +++ b/frontend/app/shared/services/schemas.service.spec.ts @@ -613,8 +613,12 @@ describe('SchemasService', () => { lastModifiedBy: `modifier${id}`, properties: { label: `label${id}${suffix}`, - hints: `hints${id}${suffix}`, - tags: [`tags${id}${suffix}`] + contentsSidebarUrl: `url/to/contents/${id}${suffix}`, + contentSidebarUrl: `url/to/content/${id}${suffix}`, + tags: [ + `tags${id}${suffix}` + ], + hints: `hints${id}${suffix}` }, version: `${id}`, _links: { @@ -637,6 +641,8 @@ describe('SchemasService', () => { version: `${id}`, properties: { label: `label${id}${suffix}`, + contentsSidebarUrl: `url/to/contents/${id}${suffix}`, + contentSidebarUrl: `url/to/content/${id}${suffix}`, tags: [ `tags${id}${suffix}` ], @@ -811,6 +817,18 @@ describe('SchemasService', () => { } }); +function createSchemaProperties(id: number, suffix = '') { + return new SchemaPropertiesDto( + `label${id}${suffix}`, + `hints${id}${suffix}`, + `url/to/contents/${id}${suffix}`, + `url/to/content/${id}${suffix}`, + [ + `tags${id}${suffix}` + ] + ); +} + export function createSchema(id: number, suffix = '') { const links: ResourceLinks = { update: { method: 'PUT', href: `/schemas/${id}` } @@ -820,7 +838,7 @@ export function createSchema(id: number, suffix = '') { `schema-id${id}`, `schema-name${id}${suffix}`, `category${id}${suffix}`, - new SchemaPropertiesDto(`label${id}${suffix}`, `hints${id}${suffix}`, [`tags${id}${suffix}`]), + createSchemaProperties(id, suffix), id % 2 === 0, id % 3 === 0, DateTime.parseISO(`${id % 1000 + 2000}-12-12T10:10:00Z`), `creator${id}`, @@ -837,7 +855,7 @@ export function createSchemaDetails(id: number, suffix = '') { `schema-id${id}`, `schema-name${id}${suffix}`, `category${id}${suffix}`, - new SchemaPropertiesDto(`label${id}${suffix}`, `hints${id}${suffix}`, [`tags${id}${suffix}`]), + createSchemaProperties(id, suffix), id % 2 === 0, id % 3 === 0, DateTime.parseISO(`${id % 1000 + 2000}-12-12T10:10:00Z`), `creator${id}`, diff --git a/frontend/app/shared/services/schemas.service.ts b/frontend/app/shared/services/schemas.service.ts index 712d4b837..1b39a7fa1 100644 --- a/frontend/app/shared/services/schemas.service.ts +++ b/frontend/app/shared/services/schemas.service.ts @@ -323,6 +323,8 @@ export class SchemaPropertiesDto { constructor( public readonly label?: string, public readonly hints?: string, + public readonly contentsSidebarUrl?: string, + public readonly contentSidebarUrl?: string, public readonly tags?: ReadonlyArray ) { } @@ -362,6 +364,8 @@ export interface SynchronizeSchemaDto { export interface UpdateSchemaDto { readonly label?: string; readonly hints?: string; + readonly contentsSidebarUrl?: string; + readonly contentSidebarUrl?: string; readonly tags?: ReadonlyArray; } @@ -698,7 +702,7 @@ function parseSchemas(response: any) { item.id, item.name, item.category, - new SchemaPropertiesDto(item.properties.label, item.properties.hints, item.properties.tags), + parseProperties(item.properties), item.isSingleton, item.isPublished, DateTime.parseISO(item.created), item.createdBy, @@ -713,13 +717,11 @@ function parseSchemas(response: any) { function parseSchemaWithDetails(response: any) { const fields = response.fields.map((item: any) => parseField(item)); - const properties = new SchemaPropertiesDto(response.properties.label, response.properties.hints, response.properties.tags); - return new SchemaDetailsDto(response._links, response.id, response.name, response.category, - properties, + parseProperties(response.properties), response.isSingleton, response.isPublished, DateTime.parseISO(response.created), response.createdBy, @@ -733,6 +735,15 @@ function parseSchemaWithDetails(response: any) { response.previewUrls || {}); } +function parseProperties(response: any) { + return new SchemaPropertiesDto( + response.label, + response.hints, + response.contentsSidebarUrl, + response.contentSidebarUrl, + response.tags); +} + export function parseField(item: any) { const propertiesDto = createProperties( diff --git a/frontend/app/shared/state/schemas.forms.ts b/frontend/app/shared/state/schemas.forms.ts index c40f4af14..882d083d9 100644 --- a/frontend/app/shared/state/schemas.forms.ts +++ b/frontend/app/shared/state/schemas.forms.ts @@ -232,6 +232,8 @@ export class EditSchemaForm extends Form
-

Font Name: icomoon (Glyphs: 136)

+

Font Name: icomoon (Glyphs: 137)

-

Grid Size: 24

+

Grid Size: 14

- - icon-enter + + icon-plugin
- - + +
liga: @@ -29,12 +29,12 @@
- - icon-zoom_out + + icon-angle-double-right
- - + +
liga: @@ -43,12 +43,12 @@
- - icon-zoom_in + + icon-angle-double-left
- - + +
liga: @@ -57,12 +57,12 @@
- - icon-flip + + icon-filter-filled
- - + +
liga: @@ -71,12 +71,12 @@
- - icon-rotate_right + + icon-clone
- - + +
liga: @@ -85,12 +85,12 @@
- - icon-rotate_left + + icon-control-Tags
- - + +
liga: @@ -99,12 +99,12 @@
- - icon-create_new_folder + + icon-control-Checkboxes
- - + +
liga: @@ -113,12 +113,12 @@
- - icon-folder + + icon-control-List
- - + +
liga: @@ -127,12 +127,12 @@
- - icon-help2 + + icon-control-Html
- - + +
liga: @@ -141,12 +141,12 @@
- - icon-trigger-Manual + + icon-single-content
- - + +
liga: @@ -155,12 +155,12 @@
- - icon-play-line + + icon-search-Content
- - + +
liga: @@ -169,12 +169,12 @@
- - icon-corner-down-right + + icon-multiple-content
- - + +
liga: @@ -183,12 +183,12 @@
- - icon-info-outline + + icon-type-Array
- - + +
liga: @@ -197,12 +197,12 @@
- - icon-upload-2 + + icon-exclamation
- - + +
liga: @@ -211,12 +211,12 @@
- - icon-translate + + icon-orleans
- - + +
liga: @@ -225,12 +225,12 @@
- - icon-arrow_back + + icon-document-lock
- - + +
liga: @@ -239,12 +239,12 @@
- - icon-external-link + + icon-document-unpublish
- - + +
liga: @@ -253,12 +253,12 @@
- - icon-minus-square + + icon-angle-down
- - + +
liga: @@ -267,12 +267,12 @@
- - icon-plus-square + + icon-angle-left
- - + +
liga: @@ -281,12 +281,12 @@
- - icon-drag2 + + icon-angle-right
- - + +
liga: @@ -295,12 +295,12 @@
- - icon-comments + + icon-angle-up
- - + +
liga: @@ -309,12 +309,12 @@
- - icon-backup + + icon-api
- - + +
liga: @@ -323,12 +323,12 @@
- - icon-support + + icon-assets
- - + +
liga: @@ -337,12 +337,12 @@
- - icon-control-RichText + + icon-search-Asset
- - + +
liga: @@ -351,211 +351,225 @@
- - icon-download + + icon-bug
- - + +
liga:
-
-
-

Grid Size: 14

-
+
- - icon-angle-double-right + + icon-caret-down
- - + +
liga:
-
+
- - icon-angle-double-left + + icon-caret-left
- - + +
liga:
-
+
- - icon-filter-filled + + icon-caret-right
- - + +
liga:
-
+
- - icon-clone + + icon-caret-up
- - + +
liga:
-
+
- - icon-control-Tags + + icon-contents
- - + +
liga:
-
+
- - icon-control-Checkboxes -
+ + icon-trigger-ContentChanged +
- - + +
liga:
-
+
- - icon-control-List + + icon-control-Date
- - + +
liga:
-
+
- - icon-control-Html + + icon-control-DateTime
- - + +
liga:
-
+
- - icon-single-content + + icon-control-Markdown
- - + +
liga:
-
+
- - icon-search-Content + + icon-grid
- - + +
liga:
-
+
- - icon-multiple-content + + icon-list1
- - + +
liga:
-
+
- - icon-type-Array + + icon-user-o
- - + +
liga:
-
+
- - icon-exclamation + + icon-rules
- - + +
liga:
+
+
+ + icon-search-Rule +
+
+ + +
+
+ liga: + +
+
+
+
+

Grid Size: 24

- - icon-orleans + + icon-enter
- - + +
liga: @@ -564,12 +578,12 @@
- - icon-document-lock + + icon-zoom_out
- - + +
liga: @@ -578,12 +592,12 @@
- - icon-document-unpublish + + icon-zoom_in
- - + +
liga: @@ -592,12 +606,12 @@
- - icon-angle-down + + icon-flip
- - + +
liga: @@ -606,12 +620,12 @@
- - icon-angle-left + + icon-rotate_right
- - + +
liga: @@ -620,12 +634,12 @@
- - icon-angle-right + + icon-rotate_left
- - + +
liga: @@ -634,12 +648,12 @@
- - icon-angle-up + + icon-create_new_folder
- - + +
liga: @@ -648,12 +662,12 @@
- - icon-api + + icon-folder
- - + +
liga: @@ -662,12 +676,12 @@
- - icon-assets + + icon-help2
- - + +
liga: @@ -676,12 +690,12 @@
- - icon-search-Asset + + icon-trigger-Manual
- - + +
liga: @@ -690,12 +704,12 @@
- - icon-bug + + icon-play-line
- - + +
liga: @@ -704,12 +718,12 @@
- - icon-caret-down + + icon-corner-down-right
- - + +
liga: @@ -718,12 +732,12 @@
- - icon-caret-left + + icon-info-outline
- - + +
liga: @@ -732,12 +746,12 @@
- - icon-caret-right + + icon-upload-2
- - + +
liga: @@ -746,12 +760,12 @@
- - icon-caret-up + + icon-translate
- - + +
liga: @@ -760,12 +774,12 @@
- - icon-contents + + icon-arrow_back
- - + +
liga: @@ -774,12 +788,12 @@
- - icon-trigger-ContentChanged + + icon-external-link
- - + +
liga: @@ -788,12 +802,12 @@
- - icon-control-Date + + icon-minus-square
- - + +
liga: @@ -802,12 +816,12 @@
- - icon-control-DateTime + + icon-plus-square
- - + +
liga: @@ -816,12 +830,12 @@
- - icon-control-Markdown + + icon-drag2
- - + +
liga: @@ -830,12 +844,12 @@
- - icon-grid + + icon-comments
- - + +
liga: @@ -844,12 +858,12 @@
- - icon-list1 + + icon-backup
- - + +
liga: @@ -858,12 +872,12 @@
- - icon-user-o + + icon-support
- - + +
liga: @@ -872,12 +886,12 @@
- - icon-rules + + icon-control-RichText
- - + +
liga: @@ -886,12 +900,12 @@
- - icon-search-Rule + + icon-download
- - + +
liga: diff --git a/frontend/app/theme/icomoon/fonts/icomoon.eot b/frontend/app/theme/icomoon/fonts/icomoon.eot index b29ef24de3414892fae20389c2be2c2d548bcbcc..413597637c99e6d917940b39ff6e4430bb07b5c5 100644 GIT binary patch delta 765 zcmXw%-%C_M6vyYx%>CuPt8RPUdui9}-n+l3m37@8cfGsiTNptQ5>`;;qLP8DhN9+= zmZC%oBUvN@Un2Vp_N9j?p%-^(wjp8eDXU#wWVAdz*K#rPfYsj+!r8Hjq0-9CtM3RcsO*ejximM4HsKmV ziz&v3#!Kaq7Z;>ItiHth%aapR(}=JRTKeSn5h3ugIt)+XQS~Q;xvkdwEgxz+{&A-g z`CBJpQbiN!GgDf;&4rY8T9*d`}V>T?L{T0n<_!&?HuNlwUnY5poyGS>4V3%;9 z7vgS;#o6(^;4V{E8*@# zSQTBfebKUqlRSZeAgDP_w*-oJDGxmTDmJl)iztfEXzv0h2Cj+A2NIerN}a~$;bV|R zDw6K!NxfB)O|w6d=m4s4ys&E|BuVjui7nCB#K|0~VoY0=e|7^fG@7p`h)v?(;tkyPXBbbst j6sc|BH9^}c%opwAJOYNT!WHCqafGWc7kxF~!fW^kF!ht| delta 265 zcmbR9mvP5WMz$R_3=ASQ6WPpIj&p2CpXg97xSxT6VGj@|BXDUGVz2=y}nS8 z8v_I51O|puk&M*D6ls?TQwD~zB|vp%89)I}Ce~gc{|=C^l95|d!NaB!$G}hyS#H)NPGFjN`<&5+7XtSDeu%oxeQP~`(uppciCn>y2r^EZ&61C(zo$S*EoUwbX-{zIE3n4TAIzF!*1ER(*0<9Iy3%~u9)7LX=} r+r^v@VDx04N^{1Z%`KHGOqCwSS-Dwt0c9D1xM%aNnnR2LLc>eV diff --git a/frontend/app/theme/icomoon/fonts/icomoon.svg b/frontend/app/theme/icomoon/fonts/icomoon.svg index 4091bbac1..dbfc77c9e 100644 --- a/frontend/app/theme/icomoon/fonts/icomoon.svg +++ b/frontend/app/theme/icomoon/fonts/icomoon.svg @@ -140,6 +140,7 @@ + diff --git a/frontend/app/theme/icomoon/fonts/icomoon.ttf b/frontend/app/theme/icomoon/fonts/icomoon.ttf index f50d96a39a24fed80a3e13cae3c8352af1679f3b..4fb2ed015b89aac3bb6f689f012894c6c33c8c40 100644 GIT binary patch delta 784 zcmXw1TWb?R6rQs)d+TnRwAp5ZNw?X}4NXbXTsGZC3qD9i6s1a4P;98R6_O^>QrlRG zik6~Olvu=P>mM+$q6J?HzV<-~ULFLY&lQTr>g-B~GjrxU=ljk#5A$LdcK4ux03jry zdkCZS+4HH4yW91iR^HJ)K2a&v+T-mhgdj+9d8#y9quE383eCb)dGSUmRS!}8459g* z>B-Xdz{=KDgccsszBx?`BEvqV*q#@euFNgC&qN+j{2d`CSFTPRFRjeK{(;aP#Wtyw z7HVi6E+e#*rh?E+sWSQMl( z=HTb8M&xfBhj9~C(HE*SIKC5TsDm)zgasP({0C{p0)-3f2GOL0FK_W*ifzVzcs&&MXWAjVx@6-C~!~IYb5t zmW2JUgf|*~)$~RyE>?5}#)6>aRlmU#W}9%pk#9l=#t@NZuhyFaDh9TLEd&y(B#8am z`k%)j2}HmHFOzl_cmYG}!t5m+{5>;-uep!@LK3n618T30>>T9g=6nHG o-`F-&f%x@;Nf52}|Bqwf+YF0r?M*761SM delta 303 zcmezIn{mny#(D-u1_lOhh6V;^1_S?KeItG$-u*z4JwTk0oSRs1JN|YG0|TQBke`#D zSX=<41%UhxAkC4UQ<+GO_jo`FDVP zm5khy3LZ9};) zeFMkwcz&C&4BRXrEeyAdIUm62$tx<%8GANAs8C_r%u%h#DBfk|X4M6h1qDYBV>p8t SNF@W)XA+_>m2z7~SewL4?dssrqeD9x zR_C98trQn&@bWS>hFxsH`Ke-Mf^tu&W`*JLN%_~z^z0n{5*Dzh!*qr5ySfVZ;7ZvA0QS4roHeFoiDA`~kn6 zx9Oc35@yPQesCbkyzFVlNZUD_jay*Gb6Go|PB|Ra#}n~H%G}pR#I(N_(A_*83eavO zvUVozr{PS}M-%8vI4}qa*5U~|UJ%(bWp>ciBphY3{c<3eNxLB~!&EufXXgijRtDb6 z2jq6Mb%68}tVp|`h-lV=0oxa=czD?p8tnuv7YLdn;kJnY0{JYpU<^@J_32%`K+V9n z@cEDxP$aoW-}rME6p2W zV~Gb*5y_@2x!~_PnD02$Zh{d|WXbA&Nd$p#R|%EyA`V-JM~tw_;p4!0$}ue$3>v%Y zilh+Y;6m4lSg)>Ysu}}<<2YG_6(bnT`Ji(&BuW?>&t}iz&Yx2Q_)^=EA4nneARyFS qAEt+Nuh+GOJghZNEkrqjhf%aKvt)p0YoJ=N3sv`*SZh43zJh-axRbsB delta 349 zcmX@|hw;G=Mv-!VH#Y`G1|XBW1sZ0Zk-CwbX- z{zID&Fg`Eb++G^UER(*0<9Iy3%~u9)7NA-NhTFxQ4`B4