diff --git a/src/Squidex/Squidex.csproj b/src/Squidex/Squidex.csproj index a7ffd5aa4..bc47b99f5 100644 --- a/src/Squidex/Squidex.csproj +++ b/src/Squidex/Squidex.csproj @@ -22,6 +22,10 @@ + + + + PreserveNewest @@ -81,6 +85,10 @@ + + + + <_DocumentationFile Include="$(DocumentationFile)" /> diff --git a/src/Squidex/app/framework/angular/rich-editor.component.html b/src/Squidex/app/framework/angular/rich-editor.component.html index bb84a4048..009451124 100644 --- a/src/Squidex/app/framework/angular/rich-editor.component.html +++ b/src/Squidex/app/framework/angular/rich-editor.component.html @@ -1 +1,37 @@ -
\ No newline at end of file +
+ + \ No newline at end of file diff --git a/src/Squidex/app/framework/angular/rich-editor.component.scss b/src/Squidex/app/framework/angular/rich-editor.component.scss index 07b2c640b..33c03a1e5 100644 --- a/src/Squidex/app/framework/angular/rich-editor.component.scss +++ b/src/Squidex/app/framework/angular/rich-editor.component.scss @@ -5,4 +5,32 @@ background: $color-dark-foreground; border: 1px solid $color-input; height: 30rem; +} +.asset-selector { + z-index: 65560; + + .modal-header { + background: transparent; + border-bottom: 1px solid #c5c5c5; + + .modal-title { + text-decoration: none; + color: #333; + font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; + text-shadow: none; + } + } + + .modal-content { + width: 100%; + border-radius: 0; + -webkit-border-radius: 0; + } + .modal-dialog { + max-width: 900px; + } + + .btn { + border-radius: 0; + } } \ No newline at end of file diff --git a/src/Squidex/app/framework/angular/rich-editor.component.ts b/src/Squidex/app/framework/angular/rich-editor.component.ts index 4e95a8152..cb742dc84 100644 --- a/src/Squidex/app/framework/angular/rich-editor.component.ts +++ b/src/Squidex/app/framework/angular/rich-editor.component.ts @@ -5,13 +5,16 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ -import { AfterViewInit, Component, forwardRef, ElementRef, OnDestroy, ViewChild } from '@angular/core'; -import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { AfterViewInit, Component, forwardRef, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR, FormBuilder } from '@angular/forms'; import { Types } from './../utils/types'; import { ResourceLoaderService } from './../services/resource-loader.service'; +import { AppComponentBase } from './../../shared/components/app.component-base'; +import { ModalView, AppsStoreService, AssetDto, AssetsService, ImmutableArray, DialogService, AuthService, Pager } from './../../shared/declarations-base'; + declare var tinymce: any; export const SQX_RICH_EDITOR_CONTROL_VALUE_ACCESSOR: any = { @@ -24,20 +27,42 @@ export const SQX_RICH_EDITOR_CONTROL_VALUE_ACCESSOR: any = { templateUrl: './rich-editor.component.html', providers: [SQX_RICH_EDITOR_CONTROL_VALUE_ACCESSOR] }) -export class RichEditorComponent implements ControlValueAccessor, AfterViewInit, OnDestroy { +export class RichEditorComponent extends AppComponentBase implements ControlValueAccessor, AfterViewInit, OnDestroy, OnInit { private callChange = (v: any) => { /* NOOP */ }; private callTouched = () => { /* NOOP */ }; private tinyEditor: any; private tinyInitTimer: any; private value: string; private isDisabled = false; + public assetsItems: ImmutableArray; + public assetsPager = new Pager(0, 0, 12); @ViewChild('editor') public editor: ElementRef; - constructor( - private readonly resourceLoader: ResourceLoaderService + public assetsDialog = new ModalView(); + public assetsForm = this.formBuilder.group({ + name: [''] + }); + + constructor(dialogs: DialogService, apps: AppsStoreService, authService: AuthService, + private readonly resourceLoader: ResourceLoaderService, + private readonly formBuilder: FormBuilder, + private readonly assetsService: AssetsService ) { + super(dialogs, apps, authService); + } + + public ngOnInit() { + + this.appNameOnce() + .switchMap(app => this.assetsService.getAssets(app, this.assetsPager.pageSize, this.assetsPager.skip)) + .subscribe(dtos => { + this.assetsItems = ImmutableArray.of(dtos.items); + this.assetsPager = this.assetsPager.setCount(dtos.total); + }, error => { + this.notifyError(error); + }); } public ngOnDestroy() { @@ -74,7 +99,9 @@ export class RichEditorComponent implements ControlValueAccessor, AfterViewInit, self.tinyEditor.setContent(this.value || ''); }, 500); }, - removed_menuitems: 'newdocument', plugins: 'code', target: this.editor.nativeElement + removed_menuitems: 'newdocument', plugins: 'code,image', target: this.editor.nativeElement, file_picker_types: 'image', file_picker_callback: (cb: any, value: any, meta: any) => { + self.assetsDialog.show(); + } }); }); } @@ -102,4 +129,13 @@ export class RichEditorComponent implements ControlValueAccessor, AfterViewInit, public registerOnTouched(fn: any) { this.callTouched = fn; } + + public selecteAsset() { + console.log('Selecting asset ' + this.assetsForm.controls['name'].value); + } + + public cancelSelectAsset() { + console.log('asset selection canceled'); + this.assetsDialog.hide(); + } } \ No newline at end of file diff --git a/src/Squidex/app/framework/declarations.ts b/src/Squidex/app/framework/declarations.ts index 40c8b1d87..188559d86 100644 --- a/src/Squidex/app/framework/declarations.ts +++ b/src/Squidex/app/framework/declarations.ts @@ -36,7 +36,7 @@ export * from './angular/panel-container.directive'; export * from './angular/parent-link.directive'; export * from './angular/popup-link.directive'; export * from './angular/progress-bar.component'; -export * from './angular/rich-editor.component'; +// export * from './angular/rich-editor.component'; export * from './angular/root-view.directive'; export * from './angular/router-utils'; export * from './angular/scroll-active.directive'; diff --git a/src/Squidex/app/framework/module.ts b/src/Squidex/app/framework/module.ts index 973635904..c23309878 100644 --- a/src/Squidex/app/framework/module.ts +++ b/src/Squidex/app/framework/module.ts @@ -54,7 +54,7 @@ import { PopupLinkDirective, ProgressBarComponent, ResourceLoaderService, - RichEditorComponent, + // RichEditorComponent, RootViewDirective, RootViewService, ScrollActiveDirective, @@ -115,7 +115,7 @@ import { ParentLinkDirective, PopupLinkDirective, ProgressBarComponent, - RichEditorComponent, + // RichEditorComponent, RootViewDirective, ScrollActiveDirective, ShortcutComponent, @@ -164,7 +164,7 @@ import { ParentLinkDirective, PopupLinkDirective, ProgressBarComponent, - RichEditorComponent, + // RichEditorComponent, RootViewDirective, ScrollActiveDirective, ShortcutComponent, diff --git a/src/Squidex/app/shared/components/asset.component.html b/src/Squidex/app/shared/components/asset.component.html index 88fc97799..96e1f7633 100644 --- a/src/Squidex/app/shared/components/asset.component.html +++ b/src/Squidex/app/shared/components/asset.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/Squidex/app/shared/components/asset.component.ts b/src/Squidex/app/shared/components/asset.component.ts index 85540c6cf..d559efba6 100644 --- a/src/Squidex/app/shared/components/asset.component.ts +++ b/src/Squidex/app/shared/components/asset.component.ts @@ -56,6 +56,9 @@ export class AssetComponent extends AppComponentBase implements OnInit { @Output() public deleting = new EventEmitter(); + @Output() + public clicked = new EventEmitter(); + @Output() public failed = new EventEmitter(); diff --git a/src/Squidex/app/shared/components/declarations.ts b/src/Squidex/app/shared/components/declarations.ts new file mode 100644 index 000000000..40c8b1d87 --- /dev/null +++ b/src/Squidex/app/shared/components/declarations.ts @@ -0,0 +1,76 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Sebastian Stehle. All rights reserved + */ + +export * from './angular/animations'; +export * from './angular/autocomplete.component'; +export * from './angular/can-deactivate.guard'; +export * from './angular/confirm-click.directive'; +export * from './angular/control-errors.component'; +export * from './angular/copy.directive'; +export * from './angular/date-time-editor.component'; +export * from './angular/date-time.pipes'; +export * from './angular/dialog-renderer.component'; +export * from './angular/dropdown.component'; +export * from './angular/file-drop.directive'; +export * from './angular/focus-on-init.directive'; +export * from './angular/geolocation-editor.component'; +export * from './angular/http-extensions-impl'; +export * from './angular/image-source.directive'; +export * from './angular/indeterminate-value.directive'; +export * from './angular/jscript-editor.component'; +export * from './angular/json-editor.component'; +export * from './angular/lowercase-input.directive'; +export * from './angular/markdown-editor.component'; +export * from './angular/modal-target.directive'; +export * from './angular/modal-view.directive'; +export * from './angular/money.pipe'; +export * from './angular/name.pipe'; +export * from './angular/numbers.pipes'; +export * from './angular/onboarding-tooltip.component'; +export * from './angular/panel.component'; +export * from './angular/panel-container.directive'; +export * from './angular/parent-link.directive'; +export * from './angular/popup-link.directive'; +export * from './angular/progress-bar.component'; +export * from './angular/rich-editor.component'; +export * from './angular/root-view.directive'; +export * from './angular/router-utils'; +export * from './angular/scroll-active.directive'; +export * from './angular/shortcut.component'; +export * from './angular/slider.component'; +export * from './angular/sorted.directive'; +export * from './angular/stars.component'; +export * from './angular/tag-editor.component'; +export * from './angular/template-wrapper.directive'; +export * from './angular/title.component'; +export * from './angular/toggle.component'; +export * from './angular/user-report.component'; +export * from './angular/validators'; +export * from './configurations'; + +export * from './services/analytics.service'; +export * from './services/clipboard.service'; +export * from './services/dialog.service'; +export * from './services/local-store.service'; +export * from './services/local-cache.service'; +export * from './services/message-bus.service'; +export * from './services/onboarding.service'; +export * from './services/resource-loader.service'; +export * from './services/root-view.service'; +export * from './services/shortcut.service'; +export * from './services/title.service'; + +export * from './utils/date-helper'; +export * from './utils/date-time'; +export * from './utils/duration'; +export * from './utils/immutable-array'; +export * from './utils/math-helper'; +export * from './utils/modal-view'; +export * from './utils/pager'; +export * from './utils/string-helper'; +export * from './utils/types'; +export * from './utils/version'; \ No newline at end of file diff --git a/src/Squidex/app/shared/components/rich-editor.component.html b/src/Squidex/app/shared/components/rich-editor.component.html new file mode 100644 index 000000000..2a0f03b4b --- /dev/null +++ b/src/Squidex/app/shared/components/rich-editor.component.html @@ -0,0 +1,29 @@ +
+ + \ No newline at end of file diff --git a/src/Squidex/app/shared/components/rich-editor.component.scss b/src/Squidex/app/shared/components/rich-editor.component.scss new file mode 100644 index 000000000..33c03a1e5 --- /dev/null +++ b/src/Squidex/app/shared/components/rich-editor.component.scss @@ -0,0 +1,36 @@ +@import '_mixins'; +@import '_vars'; + +.editor { + background: $color-dark-foreground; + border: 1px solid $color-input; + height: 30rem; +} +.asset-selector { + z-index: 65560; + + .modal-header { + background: transparent; + border-bottom: 1px solid #c5c5c5; + + .modal-title { + text-decoration: none; + color: #333; + font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; + text-shadow: none; + } + } + + .modal-content { + width: 100%; + border-radius: 0; + -webkit-border-radius: 0; + } + .modal-dialog { + max-width: 900px; + } + + .btn { + border-radius: 0; + } +} \ No newline at end of file diff --git a/src/Squidex/app/shared/components/rich-editor.component.ts b/src/Squidex/app/shared/components/rich-editor.component.ts new file mode 100644 index 000000000..ed8714654 --- /dev/null +++ b/src/Squidex/app/shared/components/rich-editor.component.ts @@ -0,0 +1,143 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Sebastian Stehle. All rights reserved + */ + +import { AfterViewInit, Component, forwardRef, ElementRef, OnDestroy, ViewChild } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR, FormBuilder } from '@angular/forms'; + +import { Types, ResourceLoaderService } from 'framework'; +import { AppComponentBase } from './app.component-base'; +import { ModalView, AppsStoreService, AssetDto, AssetsService, ImmutableArray, DialogService, AuthService, Pager } from './../declarations-base'; + +declare var tinymce: any; + +export const SQX_RICH_EDITOR_CONTROL_VALUE_ACCESSOR: any = { + provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => RichEditorComponent), multi: true +}; + +@Component({ + selector: 'sqx-rich-editor', + styleUrls: ['./rich-editor.component.scss'], + templateUrl: './rich-editor.component.html', + providers: [SQX_RICH_EDITOR_CONTROL_VALUE_ACCESSOR] +}) +export class RichEditorComponent extends AppComponentBase implements ControlValueAccessor, AfterViewInit, OnDestroy { + private callChange = (v: any) => { /* NOOP */ }; + private callTouched = () => { /* NOOP */ }; + private tinyEditor: any; + private tinyInitTimer: any; + private value: string; + private isDisabled = false; + public assetsItems: ImmutableArray; + public assetsPager = new Pager(0, 0, 12); + + @ViewChild('editor') + public editor: ElementRef; + + public assetsDialog = new ModalView(); + public assetsForm = this.formBuilder.group({ + name: [''] + }); + + constructor(dialogs: DialogService, apps: AppsStoreService, authService: AuthService, + private readonly resourceLoader: ResourceLoaderService, + private readonly formBuilder: FormBuilder, + private readonly assetsService: AssetsService + ) { + super(dialogs, apps, authService); + } + + private load() { + this.appNameOnce() + .switchMap(app => this.assetsService.getAssets(app, this.assetsPager.pageSize, this.assetsPager.skip)) + .subscribe(dtos => { + this.assetsItems = ImmutableArray.of(dtos.items); + this.assetsPager = this.assetsPager.setCount(dtos.total); + }, error => { + this.notifyError(error); + }); + } + + public ngOnDestroy() { + clearTimeout(this.tinyInitTimer); + + tinymce.remove(this.editor); + } + + public ngAfterViewInit() { + const self = this; + + this.resourceLoader.loadScript('https://cdnjs.cloudflare.com/ajax/libs/tinymce/4.5.4/tinymce.min.js').then(() => { + tinymce.init({ + setup: (editor: any) => { + self.tinyEditor = editor; + self.tinyEditor.setMode(this.isDisabled ? 'readonly' : 'design'); + + self.tinyEditor.on('change', () => { + const value = editor.getContent(); + + if (this.value !== value) { + this.value = value; + + self.callChange(value); + } + }); + + self.tinyEditor.on('blur', () => { + self.callTouched(); + }); + + this.tinyInitTimer = + setTimeout(() => { + self.tinyEditor.setContent(this.value || ''); + }, 500); + }, + removed_menuitems: 'newdocument', plugins: 'code,image', target: this.editor.nativeElement, file_picker_types: 'image', file_picker_callback: (cb: any, value: any, meta: any) => { + self.load(); + self.assetsDialog.show(); + } + }); + }); + } + + public writeValue(value: string) { + this.value = Types.isString(value) ? value : ''; + + if (this.tinyEditor) { + this.tinyEditor.setContent(this.value); + } + } + + public setDisabledState(isDisabled: boolean): void { + this.isDisabled = isDisabled; + + if (this.tinyEditor) { + this.tinyEditor.setMode(isDisabled ? 'readonly' : 'design'); + } + } + + public registerOnChange(fn: any) { + this.callChange = fn; + } + + public registerOnTouched(fn: any) { + this.callTouched = fn; + } + + public selecteAsset() { + console.log('Selecting asset ' + this.assetsForm.controls['name'].value); + } + + public cancelSelectAsset() { + console.log('asset selection canceled'); + this.assetsDialog.hide(); + } + + public onAssetClicked(asset: AssetDto) { + console.log('Asset clicked on'); + console.log(asset); + } +} \ No newline at end of file diff --git a/src/Squidex/app/shared/declarations.ts b/src/Squidex/app/shared/declarations.ts index 3e4313916..5fc29bd25 100644 --- a/src/Squidex/app/shared/declarations.ts +++ b/src/Squidex/app/shared/declarations.ts @@ -13,5 +13,6 @@ export * from './components/help.component'; export * from './components/history.component'; export * from './components/language-selector.component'; export * from './components/pipes'; +export * from './components/rich-editor.component'; export * from './declarations-base'; \ No newline at end of file diff --git a/src/Squidex/app/shared/module.ts b/src/Squidex/app/shared/module.ts index f67368770..6f229df77 100644 --- a/src/Squidex/app/shared/module.ts +++ b/src/Squidex/app/shared/module.ts @@ -57,7 +57,8 @@ import { UserManagementService, UsersProviderService, UsersService, - WebhooksService + WebhooksService, + RichEditorComponent } from './declarations'; @NgModule({ @@ -81,7 +82,8 @@ import { UserNamePipe, UserNameRefPipe, UserPicturePipe, - UserPictureRefPipe + UserPictureRefPipe, + RichEditorComponent ], exports: [ AppFormComponent, @@ -99,7 +101,8 @@ import { UserNamePipe, UserNameRefPipe, UserPicturePipe, - UserPictureRefPipe + UserPictureRefPipe, + RichEditorComponent ] }) export class SqxSharedModule { diff --git a/src/Squidex/appsettings.json b/src/Squidex/appsettings.json index eec0956f2..5d3319bce 100644 --- a/src/Squidex/appsettings.json +++ b/src/Squidex/appsettings.json @@ -3,7 +3,7 @@ /* * Set the base url of your application, to generate correct urls in background process. */ - "baseUrl": "http://localhost:5000" + "baseUrl": "http://localhost:59777" }, "ui": { @@ -154,7 +154,7 @@ * * Read More: https://docs.mongodb.com/manual/reference/connection-string/ */ - "configuration": "mongodb://localhost", + "configuration": "mongodb://localhost:27017", /* * The database for all your content collections (one collection per app). */ @@ -171,21 +171,23 @@ * Enable password auth. */ "allowPasswordAuth": true, + "adminEmail": "sow@orderboxmedia.com", + "adminPassword": "Admin!@#123", /* - * Settings for Google auth (keep empty to disable). + * Settings for Google auth (keep empty to disable).1 */ - "googleClient": "1006817248705-t3lb3ge808m9am4t7upqth79hulk456l.apps.googleusercontent.com", - "googleSecret": "QsEi-fHqkGw2_PjJmtNHf2wg", + "googleClient": "", + "googleSecret": "", /* * Settings for Github auth (keep empty to disable). */ - "githubClient": "211ea00e726baf754c78", - "githubSecret": "d0a0d0fe2c26469ae20987ac265b3a339fd73132", + "githubClient": "", + "githubSecret": "", /* * Settings for Microsoft auth (keep empty to disable). */ - "microsoftClient": "b55da740-6648-4502-8746-b9003f29d5f1", - "microsoftSecret": "idWbANxNYEF4cB368WXJhjN", + "microsoftClient": "", + "microsoftSecret": "", /* * Lock new users automatically, the administrator must unlock them. */