Browse Source

Fixes

pull/990/head
Sebastian 3 years ago
parent
commit
ec7359af2f
  1. 23
      backend/src/Squidex.Web/ClearCookiesAttribute.cs
  2. 1
      backend/src/Squidex/Areas/IdentityServer/Controllers/Account/AccountController.cs
  3. 8
      frontend/src/app/features/assets/pages/assets-page.component.html
  4. 11
      frontend/src/app/features/assets/pages/assets-page.component.ts
  5. 7
      frontend/src/app/framework/angular/forms/editors/code-editor.stories.ts
  6. 64
      frontend/src/app/framework/angular/long-hover.directive.stories.ts
  7. 70
      frontend/src/app/framework/angular/long-hover.directive.ts
  8. 1
      frontend/src/app/framework/declarations.ts
  9. 4
      frontend/src/app/framework/module.ts
  10. 6
      frontend/src/app/shared/components/assets/asset-path.component.scss
  11. 3
      frontend/src/app/shared/components/assets/assets-list.component.html
  12. 2
      frontend/src/app/shared/components/contents/content-value.component.scss
  13. 5
      frontend/src/app/shared/components/contents/content-value.component.ts
  14. 4
      frontend/src/app/shared/state/contents.forms.spec.ts
  15. 4
      frontend/src/app/shared/state/contents.forms.visitors.spec.ts
  16. 6
      frontend/src/app/shared/state/contents.forms.visitors.ts

23
backend/src/Squidex.Web/ClearCookiesAttribute.cs

@ -1,23 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Microsoft.AspNetCore.Mvc.Filters;
namespace Squidex.Web;
public sealed class ClearCookiesAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
var cookies = context.HttpContext.Response.Cookies;
foreach (var cookie in context.HttpContext.Request.Cookies.Keys)
{
cookies.Delete(cookie);
}
}
}

1
backend/src/Squidex/Areas/IdentityServer/Controllers/Account/AccountController.cs

@ -143,7 +143,6 @@ public sealed class AccountController : IdentityServerController
[HttpGet]
[Route("account/login/")]
[ClearCookies]
public Task<IActionResult> Login(string? returnUrl = null)
{
return LoginViewAsync(returnUrl, true, false);

8
frontend/src/app/features/assets/pages/assets-page.component.html

@ -22,7 +22,7 @@
<div class="col-6">
<sqx-search-form formClass="form" placeholder="{{ 'assets.searchByName' | sqxTranslate }}" fieldExample="fileSize"
[enableShortcut]="true"
[queries]="queries"
[queries]="listQueries"
[queriesTypes]="'common.assets' | sqxTranslate"
[query]="assetsState.query | async"
(queryChange)="search($event)">
@ -33,10 +33,10 @@
</div>
<div class="col-auto">
<div class="btn-group">
<button type="button" class="btn btn-secondary btn-toggle" [class.btn-primary]="isListView" [disabled]="isListView" (click)="changeView(true)">
<button type="button" class="btn btn-secondary btn-toggle" [class.btn-primary]="listMode" [disabled]="listMode" (click)="changeView(true)">
<i class="icon-list"></i>
</button>
<button type="button" class="btn btn-secondary btn-toggle" [class.btn-primary]="!isListView" [disabled]="!isListView" (click)="changeView(false)">
<button type="button" class="btn btn-secondary btn-toggle" [class.btn-primary]="!listMode" [disabled]="!listMode" (click)="changeView(false)">
<i class="icon-grid"></i>
</button>
</div>
@ -60,7 +60,7 @@
[assetsState]="assetsState"
(edit)="editStart($event)"
[isDisabled]="false"
[isListView]="isListView"
[isListView]="listMode"
[showFolderIcon]="path.length === 0">
</sqx-assets-list>
</div>

11
frontend/src/app/features/assets/pages/assets-page.component.ts

@ -18,13 +18,12 @@ import { Settings } from '@app/shared/state/settings';
],
})
export class AssetsPageComponent extends ResourceOwner implements OnInit {
public queries = new Queries(this.uiState, 'assets');
public editAsset?: AssetDto;
public addAssetFolderDialog = new DialogModel();
public listQueries = new Queries(this.uiState, 'assets');
public listMode = false;
public isListView = false;
public addAssetFolderDialog = new DialogModel();
constructor(
public readonly assetsRoute: Router2State,
@ -34,7 +33,7 @@ export class AssetsPageComponent extends ResourceOwner implements OnInit {
) {
super();
this.isListView = this.localStore.getBoolean(Settings.Local.ASSETS_MODE);
this.listMode = this.localStore.getBoolean(Settings.Local.ASSETS_MODE);
}
public ngOnInit() {
@ -84,7 +83,7 @@ export class AssetsPageComponent extends ResourceOwner implements OnInit {
}
public changeView(isListView: boolean) {
this.isListView = isListView;
this.listMode = isListView;
this.localStore.setBoolean(Settings.Local.ASSETS_MODE, isListView);
}

7
frontend/src/app/framework/angular/forms/editors/code-editor.stories.ts

@ -8,7 +8,7 @@
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { moduleMetadata } from '@storybook/angular';
import { Meta, Story } from '@storybook/angular/types-6-0';
import { CodeEditorComponent, SqxFrameworkModule } from '@app/framework';
import { CodeEditorComponent, ScriptCompletions, SqxFrameworkModule } from '@app/framework';
export default {
title: 'Framework/CodeEditor',
@ -74,15 +74,18 @@ const SingleLineTemplate: Story<CodeEditorComponent & { model: any }> = (args: C
`,
});
const COMPLETIONS = [{
const COMPLETIONS: ScriptCompletions = [{
path: 'path1',
description: 'Test1 Path',
type: 'Any',
}, {
path: 'path2',
description: 'Test2 Path',
type: 'Array',
}, {
path: 'path3',
description: 'Test3 Path',
type: 'String',
}];
export const Default = Template.bind({});

64
frontend/src/app/framework/angular/long-hover.directive.stories.ts

@ -0,0 +1,64 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { action } from '@storybook/addon-actions';
import { moduleMetadata } from '@storybook/angular';
import { Meta, Story } from '@storybook/angular/types-6-0';
import { CodeEditorComponent, LongHoverDirective, SqxFrameworkModule } from '@app/framework';
export default {
title: 'Framework/LongHover',
component: CodeEditorComponent,
argTypes: {
selector: {
control: 'text',
},
hover: {
action: 'hover',
},
cancelled: {
action: 'cancelled',
},
},
decorators: [
moduleMetadata({
imports: [
BrowserAnimationsModule,
SqxFrameworkModule,
SqxFrameworkModule.forRoot(),
],
}),
],
} as Meta;
const Template: Story<LongHoverDirective> = (args: LongHoverDirective & any) => ({
props: args,
template: `
<div (sqxLongHover)="hover()" (longHoverCancelled)="cancelled()" [longHoverSelector]="selector">
<div style="border: 1px solid #eee; padding: 100px">
<button class="btn btn-primary">Button</button>
</div>
</div>
`,
});
export const Default = Template.bind({});
Default.args = {
hover: action('Hover') as any,
selector: '',
cancelled: action('Cancelled') as any,
};
export const Selector = Default.bind({});
Selector.args = {
hover: action('Hover') as any,
selector: 'button',
cancelled: action('Cancelled') as any,
};

70
frontend/src/app/framework/angular/long-hover.directive.ts

@ -0,0 +1,70 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Directive, EventEmitter, HostListener, Input, Output, Renderer2 } from '@angular/core';
@Directive({
selector: '[sqxLongHover]',
})
export class LongHoverDirective {
private timerOut: Function | null = null;
private timer?: any;
@Output('sqxLongHover')
public hover = new EventEmitter();
@Output('longHoverCancelled')
public cancelled = new EventEmitter();
@Input('longHoverSelector')
public selector?: string;
@Input('longHoverDuration')
public duration = 2000;
constructor(
private readonly renderer: Renderer2,
) {
}
@HostListener('mouseover', ['$event'])
public onMove(event: MouseEvent) {
if (!(event.target instanceof Element)) {
this.clearTimer();
return;
}
const isMatch = !this.selector || event.target.matches(this.selector);
if (!isMatch) {
this.clearTimer();
return;
}
if (this.timer) {
return;
}
this.timer = setTimeout(() => {
this.hover.emit();
}, this.duration);
this.timerOut = this.renderer.listen(event.target, 'mouseleave', () => {
this.clearTimer();
});
}
private clearTimer() {
if (this.timer) {
clearTimeout(this.timer);
this.cancelled.emit();
this.timer = null;
this.timerOut?.();
this.timerOut = null;
}
}
}

1
frontend/src/app/framework/declarations.ts

@ -53,6 +53,7 @@ export * from './angular/language-selector.component';
export * from './angular/layout-container.directive';
export * from './angular/layout.component';
export * from './angular/list-view.component';
export * from './angular/long-hover.directive';
export * from './angular/loader.component';
export * from './angular/markdown.directive';
export * from './angular/modals/dialog-renderer.component';

4
frontend/src/app/framework/module.ts

@ -11,7 +11,7 @@ import { ErrorHandler, ModuleWithProviders, NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { ColorPickerModule } from 'ngx-color-picker';
import { AutocompleteComponent, AvatarComponent, CachingInterceptor, CanDeactivateGuard, CheckboxGroupComponent, ClipboardService, CodeComponent, CodeEditorComponent, ColorPickerComponent, CompensateScrollbarDirective, ConfirmClickDirective, ControlErrorsComponent, ControlErrorsMessagesComponent, CopyDirective, DarkenPipe, DatePipe, DateTimeEditorComponent, DayOfWeekPipe, DayPipe, DialogRendererComponent, DialogService, DisplayNamePipe, DropdownComponent, DropdownMenuComponent, DurationPipe, EditableTitleComponent, ExternalLinkDirective, FileDropDirective, FileSizePipe, FocusOnInitDirective, FormAlertComponent, FormErrorComponent, FormHintComponent, FromNowPipe, FullDateTimePipe, GlobalErrorHandler, HighlightPipe, HoverBackgroundDirective, IfOnceDirective, ImageSourceDirective, ImageUrlDirective, IndeterminateValueDirective, ISODatePipe, JoinPipe, KeysPipe, KNumberPipe, LanguageSelectorComponent, LayoutComponent, LayoutContainerDirective, LightenPipe, ListViewComponent, LoaderComponent, LoadingInterceptor, LoadingService, LocalizedInputComponent, LocalStoreService, MarkdownDirective, MarkdownInlinePipe, MarkdownPipe, MessageBus, ModalDialogComponent, ModalDirective, ModalPlacementDirective, MonthPipe, OnboardingService, OnboardingTooltipComponent, PagerComponent, ParentLinkDirective, ProgressBarComponent, RadioGroupComponent, ResizedDirective, ResizeService, ResourceLoaderService, RootViewComponent, SafeHtmlPipe, SafeResourceUrlPipe, SafeUrlPipe, ScrollActiveDirective, ShortcutComponent, ShortcutDirective, ShortcutService, ShortDatePipe, ShortTimePipe, StarsComponent, StatusIconComponent, StopClickDirective, StopDragDirective, SyncScollingDirective, SyncWidthDirective, TabRouterlinkDirective, TagEditorComponent, TemplateWrapperDirective, TempService, TitleComponent, TitleService, ToggleComponent, ToolbarComponent, TooltipDirective, TransformInputDirective, TranslatePipe, VideoPlayerComponent } from './declarations';
import { AutocompleteComponent, AvatarComponent, CachingInterceptor, CanDeactivateGuard, CheckboxGroupComponent, ClipboardService, CodeComponent, CodeEditorComponent, ColorPickerComponent, CompensateScrollbarDirective, ConfirmClickDirective, ControlErrorsComponent, ControlErrorsMessagesComponent, CopyDirective, DarkenPipe, DatePipe, DateTimeEditorComponent, DayOfWeekPipe, DayPipe, DialogRendererComponent, DialogService, DisplayNamePipe, DropdownComponent, DropdownMenuComponent, DurationPipe, EditableTitleComponent, ExternalLinkDirective, FileDropDirective, FileSizePipe, FocusOnInitDirective, FormAlertComponent, FormErrorComponent, FormHintComponent, FromNowPipe, FullDateTimePipe, GlobalErrorHandler, HighlightPipe, HoverBackgroundDirective, IfOnceDirective, ImageSourceDirective, ImageUrlDirective, IndeterminateValueDirective, ISODatePipe, JoinPipe, KeysPipe, KNumberPipe, LanguageSelectorComponent, LayoutComponent, LayoutContainerDirective, LightenPipe, ListViewComponent, LoaderComponent, LoadingInterceptor, LoadingService, LocalizedInputComponent, LocalStoreService, LongHoverDirective, MarkdownDirective, MarkdownInlinePipe, MarkdownPipe, MessageBus, ModalDialogComponent, ModalDirective, ModalPlacementDirective, MonthPipe, OnboardingService, OnboardingTooltipComponent, PagerComponent, ParentLinkDirective, ProgressBarComponent, RadioGroupComponent, ResizedDirective, ResizeService, ResourceLoaderService, RootViewComponent, SafeHtmlPipe, SafeResourceUrlPipe, SafeUrlPipe, ScrollActiveDirective, ShortcutComponent, ShortcutDirective, ShortcutService, ShortDatePipe, ShortTimePipe, StarsComponent, StatusIconComponent, StopClickDirective, StopDragDirective, SyncScollingDirective, SyncWidthDirective, TabRouterlinkDirective, TagEditorComponent, TemplateWrapperDirective, TempService, TitleComponent, TitleService, ToggleComponent, ToolbarComponent, TooltipDirective, TransformInputDirective, TranslatePipe, VideoPlayerComponent } from './declarations';
@NgModule({
imports: [
@ -70,6 +70,7 @@ import { AutocompleteComponent, AvatarComponent, CachingInterceptor, CanDeactiva
ListViewComponent,
LoaderComponent,
LocalizedInputComponent,
LongHoverDirective,
MarkdownDirective,
MarkdownInlinePipe,
MarkdownPipe,
@ -159,6 +160,7 @@ import { AutocompleteComponent, AvatarComponent, CachingInterceptor, CanDeactiva
ListViewComponent,
LoaderComponent,
LocalizedInputComponent,
LongHoverDirective,
MarkdownDirective,
MarkdownInlinePipe,
MarkdownPipe,

6
frontend/src/app/shared/components/assets/asset-path.component.scss

@ -7,4 +7,10 @@ i {
.first {
padding-left: 0;
}
.btn {
&:active {
border-color: transparent;
}
}

3
frontend/src/app/shared/components/assets/assets-list.component.html

@ -63,7 +63,8 @@
[isDisabled]="isDisabled"
[isListView]="isListView"
(loadDone)="add(file, $event)"
(loadError)="remove(file)">
(loadError)="remove(file)"
[folderId]="assetsState.parentId">
</sqx-asset>
<sqx-asset *ngFor="let asset of assets; trackBy: trackByAsset"

2
frontend/src/app/shared/components/contents/content-value.component.scss

@ -27,6 +27,8 @@
:host ::ng-deep {
img {
background-image: $asset-background;
background-size: 10px 10px;
margin-top: -25px;
max-height: 50px;
min-height: 50px;

5
frontend/src/app/shared/components/contents/content-value.component.ts

@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input } from '@angular/core';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core';
import { StatefulComponent, TypedSimpleChanges } from '@app/framework';
import { HtmlValue, TableField, TableSettings, Types } from '@app/shared/internal';
@ -29,6 +29,9 @@ export class ContentValueComponent extends StatefulComponent<State> {
@Input()
public fields?: TableSettings;
@Output()
public preview = new EventEmitter<string>();
public get title() {
return this.isString && this.isPlain ? this.value : undefined;
}

4
frontend/src/app/shared/state/contents.forms.spec.ts

@ -193,7 +193,7 @@ describe('GetContentValue', () => {
const result = getContentValue(content, language, assetWithImageAndFileName);
expect(result).toEqual({ value: ['url/to/13', 'file13'], formatted: new HtmlValue('<img src="url/to/13?width=50&height=50" /> <span>file13</span>') });
expect(result).toEqual({ value: ['url/to/13', 'file13'], formatted: new HtmlValue('<img src="url/to/13?width=50&height=50&mode=Pad" /> <span>file13</span>', 'url/to/13') });
});
it('should resolve image url only from referenced asset', () => {
@ -209,7 +209,7 @@ describe('GetContentValue', () => {
const result = getContentValue(content, language, assetWithImage);
expect(result).toEqual({ value: ['url/to/13', 'file13'], formatted: new HtmlValue('<img src="url/to/13?width=50&height=50" />') });
expect(result).toEqual({ value: ['url/to/13', 'file13'], formatted: new HtmlValue('<img src="url/to/13?width=50&height=50&mode=Pad" />', 'url/to/13') });
});
it('should resolve filename only from referenced asset', () => {

4
frontend/src/app/shared/state/contents.forms.visitors.spec.ts

@ -397,7 +397,7 @@ describe('StringField', () => {
it('should format to preview image', () => {
const field2 = createField({ properties: createProperties('String', { editor: 'StockPhoto' }) });
expect(FieldFormatter.format(field2, 'https://images.unsplash.com/123?x', true)).toEqual(new HtmlValue('<img src="https://images.unsplash.com/123?x&q=80&fm=jpg&crop=entropy&cs=tinysrgb&h=50&fit=max" />'));
expect(FieldFormatter.format(field2, 'https://images.unsplash.com/123?x', true)).toEqual(new HtmlValue('<img src="https://images.unsplash.com/123?x&q=80&fm=jpg&crop=entropy&cs=tinysrgb&h=50&fit=max" />', 'https://images.unsplash.com/123?x'));
});
it('should not format to preview image if html not allowed', () => {
@ -409,7 +409,7 @@ describe('StringField', () => {
it('should not format to preview image if not unsplash image', () => {
const field2 = createField({ properties: createProperties('String', { editor: 'StockPhoto' }) });
expect(FieldFormatter.format(field2, 'https://images.com/123?x', true)).toEqual(new HtmlValue('<img src="https://images.com/123?x" />'));
expect(FieldFormatter.format(field2, 'https://images.com/123?x', true)).toEqual(new HtmlValue('<img src="https://images.com/123?x" />', 'https://images.com/123?x'));
});
it('should return default value from properties', () => {

6
frontend/src/app/shared/state/contents.forms.visitors.ts

@ -15,6 +15,7 @@ import { ArrayFieldPropertiesDto, AssetsFieldPropertiesDto, BooleanFieldProperti
export class HtmlValue {
constructor(
public readonly html: string,
public readonly preview?: string,
) {
}
}
@ -52,9 +53,9 @@ export function getContentValue(content: ContentDto, language: LanguageDto, fiel
const previewMode = field.properties['previewMode'];
if (previewMode === 'ImageAndFileName') {
formatted = new HtmlValue(`<img src="${value[0]}?width=50&height=50" /> <span>${value[1]}</span>`);
formatted = new HtmlValue(`<img src="${value[0]}?width=50&height=50&mode=Pad" /> <span>${value[1]}</span>`, value[0]);
} else if (previewMode === 'Image') {
formatted = new HtmlValue(`<img src="${value[0]}?width=50&height=50" />`);
formatted = new HtmlValue(`<img src="${value[0]}?width=50&height=50&mode=Pad" />`, value[0]);
} else {
formatted = value[1];
}
@ -63,7 +64,6 @@ export function getContentValue(content: ContentDto, language: LanguageDto, fiel
}
}
} else {
// eslint-disable-next-line no-multi-assign
value = formatted = '-';
}

Loading…
Cancel
Save