@@ -60,14 +72,27 @@
\ No newline at end of file
diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/string-ui.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/types/string-ui.component.ts
index 85713c746..271eb1969 100644
--- a/frontend/src/app/features/schemas/pages/schema/fields/types/string-ui.component.ts
+++ b/frontend/src/app/features/schemas/pages/schema/fields/types/string-ui.component.ts
@@ -8,7 +8,7 @@
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { Observable } from 'rxjs';
-import { FieldDto, ResourceOwner, STRING_FIELD_EDITORS, StringFieldPropertiesDto, valueProjection$ } from '@app/shared';
+import { FieldDto, ResourceOwner, STRING_FIELD_EDITORS, StringFieldPropertiesDto, valueProjection$, SchemaTagSource } from '@app/shared';
@Component({
selector: 'sqx-string-ui[field][fieldForm][properties]',
@@ -29,6 +29,13 @@ export class StringUIComponent extends ResourceOwner implements OnChanges {
public hideAllowedValues?: Observable
;
public hideInlineEditable?: Observable;
+ public hideSchemaIds?: Observable;
+
+ constructor(
+ public readonly schemasSource: SchemaTagSource,
+ ) {
+ super();
+ }
public ngOnChanges(changes: SimpleChanges) {
if (changes['fieldForm']) {
@@ -42,6 +49,9 @@ export class StringUIComponent extends ResourceOwner implements OnChanges {
this.hideInlineEditable =
valueProjection$(editor, x => !(x && (x === 'Input' || x === 'Dropdown' || x === 'Slug')));
+ this.hideSchemaIds =
+ valueProjection$(this.fieldForm.controls['isEmbeddable'], x => !x);
+
this.own(
this.hideAllowedValues.subscribe(isSelection => {
if (isSelection) {
diff --git a/frontend/src/app/shared/components/forms/markdown-editor.component.html b/frontend/src/app/shared/components/forms/markdown-editor.component.html
index 8677f53e4..0104a09d3 100644
--- a/frontend/src/app/shared/components/forms/markdown-editor.component.html
+++ b/frontend/src/app/shared/components/forms/markdown-editor.component.html
@@ -10,4 +10,13 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/app/shared/components/forms/markdown-editor.component.ts b/frontend/src/app/shared/components/forms/markdown-editor.component.ts
index 603bd0a5a..cda460cf9 100644
--- a/frontend/src/app/shared/components/forms/markdown-editor.component.ts
+++ b/frontend/src/app/shared/components/forms/markdown-editor.component.ts
@@ -8,7 +8,8 @@
import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, forwardRef, Input, Renderer2, ViewChild } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { marked } from 'marked';
-import { ApiUrlConfig, AssetDto, AssetUploaderState, DialogModel, ResourceLoaderService, StatefulControlComponent, Types, UploadCanceled } from '@app/shared/internal';
+import { ApiUrlConfig, AssetDto, AssetUploaderState, DialogModel, getContentValue, LanguageDto, ResourceLoaderService, StatefulControlComponent, Types, UploadCanceled } from '@app/shared/internal';
+import { ContentDto } from '@app/shared';
declare const SimpleMDE: any;
@@ -34,6 +35,15 @@ export class MarkdownEditorComponent extends StatefulControlComponent;
+
+ @Input()
+ public language!: LanguageDto;
+
+ @Input()
+ public languages!: ReadonlyArray;
+
@Input()
public folderId?: string;
@@ -53,6 +63,8 @@ export class MarkdownEditorComponent extends StatefulControlComponent {
+ private showAssetSelector = () => {
if (this.snapshot.isDisabled) {
return;
}
@@ -86,6 +98,14 @@ export class MarkdownEditorComponent extends StatefulControlComponent {
+ if (this.snapshot.isDisabled) {
+ return;
+ }
+
+ this.contentsDialog.show();
+ };
+
public ngAfterViewInit() {
Promise.all([
this.resourceLoader.loadLocalStyle('dependencies/simplemde/simplemde.min.css'),
@@ -170,10 +190,17 @@ export class MarkdownEditorComponent extends StatefulControlComponent 0 ?
+ {
+ name: 'contents',
+ action: this.showContentsSelector,
+ className: 'icon-contents icon-bold',
+ title: 'Insert Contents',
+ } : null,
],
element: this.editor.nativeElement,
});
@@ -204,7 +231,7 @@ export class MarkdownEditorComponent extends StatefulControlComponent) {
- const content = this.buildMarkups(assets);
+ const content = this.buildAssetsMarkup(assets);
if (content.length > 0) {
this.simplemde.codemirror.replaceSelection(content);
@@ -213,6 +240,16 @@ export class MarkdownEditorComponent extends StatefulControlComponent) {
+ const content = this.buildContentsMarkup(contents);
+
+ if (content.length > 0) {
+ this.simplemde.codemirror.replaceSelection(content);
+ }
+
+ this.contentsDialog.hide();
+ }
+
public insertFiles(files: ReadonlyArray) {
const doc = this.simplemde.codemirror.getDoc();
@@ -251,7 +288,7 @@ export class MarkdownEditorComponent extends StatefulControlComponent {
if (Types.is(asset, AssetDto)) {
- replaceText(this.buildMarkup(asset));
+ replaceText(this.buildAssetMarkup(asset));
}
},
error: error => {
@@ -274,17 +311,39 @@ export class MarkdownEditorComponent extends StatefulControlComponent) {
+ let markup = '';
for (const asset of assets) {
- content += this.buildMarkup(asset);
+ markup += this.buildAssetMarkup(asset);
}
- return content;
+ return markup;
+ }
+
+ private buildContentsMarkup(contents: ReadonlyArray) {
+ let markup = '';
+
+ for (const content of contents) {
+ markup += this.buildContentMarkup(content);
+ }
+
+ return markup;
+ }
+
+ private buildContentMarkup(content: ContentDto) {
+ const name =
+ content.referenceFields
+ .map(f => getContentValue(content, this.language, f, false))
+ .map(v => v.formatted)
+ .defined()
+ .join(', ')
+ || 'content';
+
+ return `[${name}](${this.apiUrl.buildUrl(content._links['self'].href)}')`;
}
- private buildMarkup(asset: AssetDto) {
+ private buildAssetMarkup(asset: AssetDto) {
const name = asset.fileNameWithoutExtension;
if (asset.type === 'Image' || asset.mimeType === 'image/svg+xml' || asset.fileName.endsWith('.svg')) {
diff --git a/frontend/src/app/shared/components/forms/rich-editor.component.html b/frontend/src/app/shared/components/forms/rich-editor.component.html
index 2fb4d1bf8..72f9aa5de 100644
--- a/frontend/src/app/shared/components/forms/rich-editor.component.html
+++ b/frontend/src/app/shared/components/forms/rich-editor.component.html
@@ -6,4 +6,13 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/app/shared/components/forms/rich-editor.component.ts b/frontend/src/app/shared/components/forms/rich-editor.component.ts
index 627115039..c3921ba3b 100644
--- a/frontend/src/app/shared/components/forms/rich-editor.component.ts
+++ b/frontend/src/app/shared/components/forms/rich-editor.component.ts
@@ -7,7 +7,8 @@
import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, forwardRef, Input, OnDestroy, Output, ViewChild } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
-import { ApiUrlConfig, AssetDto, AssetUploaderState, DialogModel, LocalizerService, ResourceLoaderService, StatefulControlComponent, Types, UploadCanceled } from '@app/shared/internal';
+import { ContentDto } from '@app/shared';
+import { ApiUrlConfig, AssetDto, AssetUploaderState, DialogModel, getContentValue, LanguageDto, ResourceLoaderService, StatefulControlComponent, Types, UploadCanceled } from '@app/shared/internal';
declare const tinymce: any;
@@ -31,6 +32,15 @@ export class RichEditorComponent extends StatefulControlComponent<{}, string> im
@Output()
public assetPluginClick = new EventEmitter();
+ @Input()
+ public schemaIds?: ReadonlyArray;
+
+ @Input()
+ public language!: LanguageDto;
+
+ @Input()
+ public languages!: ReadonlyArray;
+
@Input()
public folderId = '';
@@ -44,11 +54,12 @@ export class RichEditorComponent extends StatefulControlComponent<{}, string> im
public assetsDialog = new DialogModel();
+ public contentsDialog = new DialogModel();
+
constructor(changeDetector: ChangeDetectorRef,
private readonly apiUrl: ApiUrlConfig,
private readonly assetUploader: AssetUploaderState,
private readonly resourceLoader: ResourceLoaderService,
- private readonly localizer: LocalizerService,
) {
super(changeDetector, {});
}
@@ -82,7 +93,7 @@ export class RichEditorComponent extends StatefulControlComponent<{}, string> im
});
}
- private showSelector = () => {
+ private showAssetsSelector = () => {
if (this.snapshot.isDisabled) {
return;
}
@@ -90,6 +101,14 @@ export class RichEditorComponent extends StatefulControlComponent<{}, string> im
this.assetsDialog.show();
};
+ private showContentsSelector = () => {
+ if (this.snapshot.isDisabled) {
+ return;
+ }
+
+ this.contentsDialog.show();
+ };
+
private getEditorOptions(target: any): any {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this;
@@ -117,12 +136,21 @@ export class RichEditorComponent extends StatefulControlComponent<{}, string> im
setup: (editor: any) => {
editor.ui.registry.addButton('assets', {
- onAction: self.showSelector,
+ onAction: self.showAssetsSelector,
icon: 'gallery',
text: '',
- tooltip: this.localizer.getOrKey('assets.insertAssets'),
+ tooltip: 'Insert Assets',
});
+ if (this.schemaIds && this.schemaIds.length > 0) {
+ editor.ui.registry.addButton('contents', {
+ onAction: self.showContentsSelector,
+ icon: 'duplicate',
+ text: '',
+ tooltip: 'Insert Contents',
+ });
+ }
+
editor.on('init', () => {
self.tinyEditor = editor;
@@ -222,7 +250,7 @@ export class RichEditorComponent extends StatefulControlComponent<{}, string> im
}
public insertAssets(assets: ReadonlyArray) {
- const content = this.buildMarkups(assets);
+ const content = this.buildAssetsMarkup(assets);
if (content.length > 0) {
this.tinyEditor.execCommand('mceInsertContent', false, content);
@@ -231,6 +259,16 @@ export class RichEditorComponent extends StatefulControlComponent<{}, string> im
this.assetsDialog.hide();
}
+ public insertContents(contents: ReadonlyArray) {
+ const content = this.buildContentsMarkup(contents);
+
+ if (content.length > 0) {
+ this.tinyEditor.execCommand('mceInsertContent', false, content);
+ }
+
+ this.contentsDialog.hide();
+ }
+
public insertFiles(files: ReadonlyArray) {
for (const file of files) {
this.uploadFile(file);
@@ -252,7 +290,7 @@ export class RichEditorComponent extends StatefulControlComponent<{}, string> im
.subscribe({
next: asset => {
if (Types.is(asset, AssetDto)) {
- replaceText(this.buildMarkup(asset));
+ replaceText(this.buildAssetMarkup(asset));
}
},
error: error => {
@@ -263,17 +301,39 @@ export class RichEditorComponent extends StatefulControlComponent<{}, string> im
});
}
- private buildMarkups(assets: readonly AssetDto[]) {
- let content = '';
+ private buildAssetsMarkup(assets: ReadonlyArray) {
+ let markup = '';
for (const asset of assets) {
- content += this.buildMarkup(asset);
+ markup += this.buildAssetMarkup(asset);
}
- return content;
+ return markup;
+ }
+
+ private buildContentsMarkup(contents: ReadonlyArray) {
+ let markup = '';
+
+ for (const content of contents) {
+ markup += this.buildContentMarkup(content);
+ }
+
+ return markup;
+ }
+
+ private buildContentMarkup(content: ContentDto) {
+ const name =
+ content.referenceFields
+ .map(f => getContentValue(content, this.language, f, false))
+ .map(v => v.formatted)
+ .defined()
+ .join(', ')
+ || 'content';
+
+ return `${name}`;
}
- private buildMarkup(asset: AssetDto) {
+ private buildAssetMarkup(asset: AssetDto) {
const name = asset.fileNameWithoutExtension;
if (asset.type === 'Image' || asset.mimeType === 'image/svg+xml' || asset.fileName.endsWith('.svg')) {
@@ -295,5 +355,5 @@ const DEFAULT_PROPS = {
max_height: 800,
removed_menuitems: 'newdocument',
resize: true,
- toolbar: 'undo redo | styleselect | bold italic | alignleft aligncenter | bullist numlist outdent indent | link image media | assets',
+ toolbar: 'undo redo | styleselect | bold italic | alignleft aligncenter | bullist numlist outdent indent | link image media | assets contents',
};
diff --git a/frontend/src/app/shared/services/schemas.types.ts b/frontend/src/app/shared/services/schemas.types.ts
index 88a916870..1c64e8f75 100644
--- a/frontend/src/app/shared/services/schemas.types.ts
+++ b/frontend/src/app/shared/services/schemas.types.ts
@@ -429,22 +429,24 @@ export class StringFieldPropertiesDto extends FieldPropertiesDto {
public readonly fieldType = 'String';
public readonly allowedValues?: ReadonlyArray;
+ public readonly contentType?: StringContentType;
public readonly createEnum: boolean = false;
public readonly defaultValue?: string;
public readonly defaultValues?: DefaultValue;
public readonly editor: StringFieldEditor = 'Input';
+ public readonly folderId?: string;
public readonly inlineEditable: boolean = false;
+ public readonly isEmbeddable: boolean = false;
public readonly isUnique: boolean = false;
- public readonly folderId?: string;
+ public readonly maxCharacters?: number;
public readonly maxLength?: number;
- public readonly minLength?: number;
public readonly maxWords?: number;
- public readonly minWords?: number;
- public readonly maxCharacters?: number;
public readonly minCharacters?: number;
- public readonly contentType?: StringContentType;
+ public readonly minLength?: number;
+ public readonly minWords?: number;
public readonly pattern?: string;
public readonly patternMessage?: string;
+ public readonly schemaIds?: ReadonlyArray;
public get isComplexUI() {
return this.editor !== 'Input' && this.editor !== 'Color' && this.editor !== 'Radio' && this.editor !== 'Slug' && this.editor !== 'TextArea';
diff --git a/frontend/src/app/shared/state/schemas.forms.ts b/frontend/src/app/shared/state/schemas.forms.ts
index 234d040de..e6267008d 100644
--- a/frontend/src/app/shared/state/schemas.forms.ts
+++ b/frontend/src/app/shared/state/schemas.forms.ts
@@ -331,6 +331,7 @@ export class EditFieldFormVisitor implements FieldPropertiesVisitor {
this.config['defaultValues'] = new FormControl(undefined);
this.config['folderId'] = new FormControl(undefined);
this.config['inlineEditable'] = new FormControl(undefined);
+ this.config['isEmbeddable'] = new FormControl(undefined);
this.config['isUnique'] = new FormControl(undefined);
this.config['maxCharacters'] = new FormControl(undefined);
this.config['maxLength'] = new FormControl(undefined);
@@ -340,6 +341,7 @@ export class EditFieldFormVisitor implements FieldPropertiesVisitor {
this.config['minWords'] = new FormControl(undefined);
this.config['pattern'] = new FormControl(undefined);
this.config['patternMessage'] = new FormControl(undefined);
+ this.config['schemaIds'] = new FormControl(undefined);
}
public visitTags() {