Browse Source

Content view restructured.

pull/272/head
Sebastian Stehle 8 years ago
parent
commit
65280bb867
  1. 11
      src/Squidex/app/features/content/module.ts
  2. 6
      src/Squidex/app/features/content/pages/content/content-page.component.html
  3. 71
      src/Squidex/app/features/content/pages/content/content-page.component.ts
  4. 65
      src/Squidex/app/features/content/pages/contents/contents-page.component.html
  5. 16
      src/Squidex/app/features/content/pages/contents/contents-page.component.scss
  6. 83
      src/Squidex/app/features/content/pages/contents/contents-page.component.ts
  7. 24
      src/Squidex/app/features/content/pages/contents/search-form.component.html
  8. 20
      src/Squidex/app/features/content/pages/contents/search-form.component.scss
  9. 29
      src/Squidex/app/features/content/pages/contents/search-form.component.ts
  10. 30
      src/Squidex/app/features/content/pages/messages.ts
  11. 8
      src/Squidex/app/features/content/shared/contents-selector.component.html
  12. 28
      src/Squidex/app/features/content/shared/contents-selector.component.scss
  13. 6
      src/Squidex/app/features/content/shared/contents-selector.component.ts
  14. 2
      src/Squidex/app/features/content/shared/references-editor.component.html
  15. 2
      src/Squidex/app/features/schemas/declarations.ts
  16. 2
      src/Squidex/app/features/schemas/module.ts
  17. 38
      src/Squidex/app/shared/guards/schema-must-exist-published.guard.ts
  18. 6
      src/Squidex/app/shared/guards/schema-must-exist.guard.ts
  19. 2
      src/Squidex/app/shared/internal.ts
  20. 4
      src/Squidex/app/shared/module.ts

11
src/Squidex/app/features/content/module.ts

@ -13,7 +13,7 @@ import {
CanDeactivateGuard, CanDeactivateGuard,
ResolveAppLanguagesGuard, ResolveAppLanguagesGuard,
ResolveContentGuard, ResolveContentGuard,
ResolvePublishedSchemaGuard, SchemaMustExistPublishedGuard,
SqxFrameworkModule, SqxFrameworkModule,
SqxSharedModule SqxSharedModule
} from '@app/shared'; } from '@app/shared';
@ -41,11 +41,16 @@ const routes: Routes = [
}, },
{ {
path: ':schemaName', path: ':schemaName',
component: ContentsPageComponent, canActivate: [SchemaMustExistPublishedGuard],
resolve: { resolve: {
schema: ResolvePublishedSchemaGuard, appLanguages: ResolveAppLanguagesGuard appLanguages: ResolveAppLanguagesGuard
}, },
children: [ children: [
{
path: '',
component: ContentsPageComponent,
canDeactivate: [CanDeactivateGuard]
},
{ {
path: 'new', path: 'new',
component: ContentPageComponent, component: ContentPageComponent,

6
src/Squidex/app/features/content/pages/content/content-page.component.html

@ -1,8 +1,12 @@
<sqx-title message="{app} | {schema} | Content" parameter1="app" parameter2="schema" [value1]="ctx.appName" [value2]="schema?.displayName"></sqx-title> <sqx-title message="{app} | {schema} | Content" parameter1="app" parameter2="schema" [value1]="ctx.appName" [value2]="schema?.displayName"></sqx-title>
<form [formGroup]="contentForm" (ngSubmit)="saveAndPublish()"> <form [formGroup]="contentForm" (ngSubmit)="saveAndPublish()">
<sqx-panel desiredWidth="53rem" showSidebar="true"> <sqx-panel desiredWidth="54rem" showSidebar="true">
<ng-container title> <ng-container title>
<a class="btn btn-link" (click)="back()">
<i class="icon-angle-left"></i>
</a>
<ng-container *ngIf="isNewMode"> <ng-container *ngIf="isNewMode">
New Content New Content
</ng-container> </ng-container>

71
src/Squidex/app/features/content/pages/content/content-page.component.ts

@ -10,13 +10,7 @@ import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
import { import { ContentVersionSelected } from './../messages';
ContentCreated,
ContentRemoved,
ContentStatusChanged,
ContentUpdated,
ContentVersionSelected
} from './../messages';
import { import {
AppContext, AppContext,
@ -27,6 +21,7 @@ import {
ContentsService, ContentsService,
fieldInvariant, fieldInvariant,
SchemaDetailsDto, SchemaDetailsDto,
SchemasState,
Version Version
} from '@app/shared'; } from '@app/shared';
@ -39,10 +34,8 @@ import {
] ]
}) })
export class ContentPageComponent implements CanComponentDeactivate, OnDestroy, OnInit { export class ContentPageComponent implements CanComponentDeactivate, OnDestroy, OnInit {
private contentStatusChangedSubscription: Subscription;
private contentDeletedSubscription: Subscription;
private contentUpdatedSubscription: Subscription;
private contentVersionSelectedSubscription: Subscription; private contentVersionSelectedSubscription: Subscription;
private selectedSchemaSubscription: Subscription;
public schema: SchemaDetailsDto; public schema: SchemaDetailsDto;
@ -57,15 +50,14 @@ export class ContentPageComponent implements CanComponentDeactivate, OnDestroy,
constructor(public readonly ctx: AppContext, constructor(public readonly ctx: AppContext,
private readonly contentsService: ContentsService, private readonly contentsService: ContentsService,
private readonly router: Router private readonly router: Router,
private readonly schemasState: SchemasState
) { ) {
} }
public ngOnDestroy() { public ngOnDestroy() {
this.contentVersionSelectedSubscription.unsubscribe(); this.contentVersionSelectedSubscription.unsubscribe();
this.contentStatusChangedSubscription.unsubscribe(); this.selectedSchemaSubscription.unsubscribe();
this.contentUpdatedSubscription.unsubscribe();
this.contentDeletedSubscription.unsubscribe();
} }
public ngOnInit() { public ngOnInit() {
@ -75,40 +67,14 @@ export class ContentPageComponent implements CanComponentDeactivate, OnDestroy,
this.loadVersion(message.version); this.loadVersion(message.version);
}); });
this.contentDeletedSubscription = this.selectedSchemaSubscription =
this.ctx.bus.of(ContentRemoved) this.schemasState.selectedSchema
.subscribe(message => { .subscribe(schema => {
if (this.content && message.content.id === this.content.id) {
this.router.navigate(['../'], { relativeTo: this.ctx.route });
}
});
this.contentUpdatedSubscription =
this.ctx.bus.of(ContentUpdated)
.subscribe(message => {
if (this.content && message.content.id === this.content.id) {
this.reloadContentForm(message.content);
}
});
this.contentStatusChangedSubscription =
this.ctx.bus.of(ContentStatusChanged)
.subscribe(message => {
if (this.content && message.content.id === this.content.id) {
this.content =
this.content.changeStatus(
message.content.scheduledTo || message.content.status,
message.content.scheduledAt,
message.content.lastModifiedBy,
message.content.version,
message.content.lastModified);
}
});
const routeData = allData(this.ctx.route); const routeData = allData(this.ctx.route);
this.setupLanguages(routeData); this.setupLanguages(routeData);
this.setupContentForm(routeData.schema); this.setupContentForm(schema!);
});
this.ctx.route.data.map(d => d.content) this.ctx.route.data.map(d => d.content)
.subscribe((content: ContentDto) => { .subscribe((content: ContentDto) => {
@ -129,7 +95,6 @@ export class ContentPageComponent implements CanComponentDeactivate, OnDestroy,
this.content = this.contentOld; this.content = this.contentOld;
this.contentOld = null; this.contentOld = null;
this.emitContentUpdated(this.content);
this.reloadContentForm(this.content); this.reloadContentForm(this.content);
} }
} }
@ -157,7 +122,6 @@ export class ContentPageComponent implements CanComponentDeactivate, OnDestroy,
this.ctx.notifyInfo('Content created successfully.'); this.ctx.notifyInfo('Content created successfully.');
this.emitContentCreated(this.content);
this.back(); this.back();
}, error => { }, error => {
this.ctx.notifyError(error); this.ctx.notifyError(error);
@ -171,7 +135,6 @@ export class ContentPageComponent implements CanComponentDeactivate, OnDestroy,
this.ctx.notifyInfo('Content saved successfully.'); this.ctx.notifyInfo('Content saved successfully.');
this.emitContentUpdated(content);
this.enableContentForm(); this.enableContentForm();
this.reloadContentForm(content); this.reloadContentForm(content);
}, error => { }, error => {
@ -204,16 +167,8 @@ export class ContentPageComponent implements CanComponentDeactivate, OnDestroy,
} }
} }
private back() { public back() {
this.router.navigate(['../'], { relativeTo: this.ctx.route, replaceUrl: true }); this.router.navigate([this.schema.name], { relativeTo: this.ctx.route.parent!.parent, replaceUrl: true });
}
private emitContentCreated(content: ContentDto) {
this.ctx.bus.emit(new ContentCreated(content));
}
private emitContentUpdated(content: ContentDto) {
this.ctx.bus.emit(new ContentUpdated(content));
} }
private disableContentForm() { private disableContentForm() {

65
src/Squidex/app/features/content/pages/contents/contents-page.component.html

@ -1,16 +1,14 @@
<sqx-title message="{app} | {schema} | Contents" parameter1="app" parameter2="schema" [value1]="ctx.appName" [value2]="schema?.displayName"></sqx-title> <sqx-title message="{app} | {schema} | Contents" parameter1="app" parameter2="schema" [value1]="ctx.appName" [value2]="schema?.displayName"></sqx-title>
<sqx-panel [desiredWidth]="isReadOnly ? '40rem' : '60rem'" contentClass="grid"> <sqx-panel desiredWidth="*" contentClass="grid">
<ng-container title> <ng-container title>
<ng-container *ngIf="!isReadOnly && !isArchive"> <ng-container *ngIf="isArchive; else noArchive">
Contents
</ng-container>
<ng-container *ngIf="isArchive">
Archive Archive
</ng-container> </ng-container>
<ng-container *ngIf="isReadOnly">
References <ng-template #noArchive>
</ng-container> Contents
</ng-template>
</ng-container> </ng-container>
<ng-container menu> <ng-container menu>
@ -19,40 +17,14 @@
</button> </button>
<sqx-shortcut keys="ctrl+shift+r" (trigger)="load(true)"></sqx-shortcut> <sqx-shortcut keys="ctrl+shift+r" (trigger)="load(true)"></sqx-shortcut>
<sqx-shortcut keys="ctrl+shift+f" (trigger)="inputFind.focus()"></sqx-shortcut>
<sqx-shortcut keys="ctrl+shift+g" (trigger)="newButton.click()" *ngIf="!isReadOnly"></sqx-shortcut>
<form class="form-inline" (ngSubmit)="search()">
<input class="form-control form-control-expandable" #inputFind [formControl]="contentsFilter" placeholder="Search for content" />
<a class="expand-search" (click)="searchModal.toggle()" #archive>
<i class="icon-caret-down"></i>
</a>
</form>
<sqx-onboarding-tooltip id="contentArchive" [for]="archive" position="bottomRight" after="60000">
Click this icon to show the advanced search menu and to show the archive!
</sqx-onboarding-tooltip>
<sqx-onboarding-tooltip id="contentFind" [for]="inputFind" position="bottomRight" after="120000">
Search for content using full text search over all fields and languages!
</sqx-onboarding-tooltip>
<div class="dropdown-menu" *sqxModalView="searchModal" [sqxModalTarget]="inputFind">
<sqx-search-form
[canArchive]="!isReadOnly"
(queryChanged)="contentsFilter.setValue($event, { emitEvent: false })"
[query]="contentsFilter.value"
(archivedChanged)="updateArchive($event)"
[archived]="isArchive">
</sqx-search-form>
</div>
<ng-container *ngIf="!isReadOnly && languages.length > 1"> <sqx-search-form (queryChanged)="search($event)" [query]="contentsQuery" enableShortcut="true"></sqx-search-form>
<ng-container *ngIf="languages.length > 1">
<sqx-language-selector class="languages-buttons" (selectedLanguageChanged)="selectLanguage($event)" [languages]="languages"></sqx-language-selector> <sqx-language-selector class="languages-buttons" (selectedLanguageChanged)="selectLanguage($event)" [languages]="languages"></sqx-language-selector>
</ng-container> </ng-container>
<button *ngIf="!isReadOnly" class="btn btn-success" #newButton routerLink="new" title="New Content (CTRL + SHIFT + G)"> <button class="btn btn-success" #newButton routerLink="new" title="New Content (CTRL + SHIFT + G)">
<i class="icon-plus"></i> New <i class="icon-plus"></i> New
</button> </button>
</ng-container> </ng-container>
@ -111,8 +83,8 @@
<div class="grid-content"> <div class="grid-content">
<div sqxIgnoreScrollbar> <div sqxIgnoreScrollbar>
<table class="table table-items table-fixed" *ngIf="contentItems" > <table class="table table-items table-fixed" *ngIf="contentItems">
<tbody *ngIf="!isReadOnly"> <tbody>
<ng-template ngFor let-content [ngForOf]="contentItems" [ngForTrackBy]="trackByContent"> <ng-template ngFor let-content [ngForOf]="contentItems" [ngForTrackBy]="trackByContent">
<tr [sqxContent]="content" [routerLink]="[content.id]" routerLinkActive="active" <tr [sqxContent]="content" [routerLink]="[content.id]" routerLinkActive="active"
[language]="languageSelected" [language]="languageSelected"
@ -129,17 +101,6 @@
<tr class="spacer"></tr> <tr class="spacer"></tr>
</ng-template> </ng-template>
</tbody> </tbody>
<tbody *ngIf="isReadOnly">
<ng-template ngFor let-content [ngForOf]="contentItems">
<tr [sqxContent]="content" dnd-draggable [dragData]="dropData(content)"
[language]="languageSelected"
[schemaFields]="contentFields"
[schema]="schema"
isReadOnly="true"></tr>
<tr class="spacer"></tr>
</ng-template>
</tbody>
</table> </table>
</div> </div>
</div> </div>
@ -150,8 +111,6 @@
</ng-container> </ng-container>
</sqx-panel> </sqx-panel>
<router-outlet></router-outlet>
<ng-container *sqxModalView="dueTimeDialog;onRoot:true"> <ng-container *sqxModalView="dueTimeDialog;onRoot:true">
<sqx-modal-dialog (close)="cancelStatusChange()"> <sqx-modal-dialog (close)="cancelStatusChange()">
<ng-container title> <ng-container title>

16
src/Squidex/app/features/content/pages/contents/contents-page.component.scss

@ -9,22 +9,6 @@
font-size: .8rem; font-size: .8rem;
} }
.form-inline {
position: relative;
}
.form-control-expandable {
padding-right: 1.5rem;
}
.expand-search {
@include absolute(8px, 8px, auto, auto);
color: $color-border-dark !important;
font-size: .9rem;
font-weight: normal;
cursor: pointer !important;
}
.selection { .selection {
background: $color-table-footer; background: $color-table-footer;
border-bottom: 2px solid $color-border; border-bottom: 2px solid $color-border;

83
src/Squidex/app/features/content/pages/contents/contents-page.component.ts

@ -6,16 +6,8 @@
*/ */
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
import { Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
import {
ContentCreated,
ContentRemoved,
ContentStatusChanged,
ContentUpdated
} from './../messages';
import { import {
allData, allData,
AppContext, AppContext,
@ -27,6 +19,7 @@ import {
ImmutableArray, ImmutableArray,
ModalView, ModalView,
Pager, Pager,
SchemasState,
SchemaDetailsDto, SchemaDetailsDto,
Versioned Versioned
} from '@app/shared'; } from '@app/shared';
@ -40,8 +33,7 @@ import {
] ]
}) })
export class ContentsPageComponent implements OnDestroy, OnInit { export class ContentsPageComponent implements OnDestroy, OnInit {
private contentCreatedSubscription: Subscription; private selectedSchemaSubscription: Subscription;
private contentUpdatedSubscription: Subscription;
public schema: SchemaDetailsDto; public schema: SchemaDetailsDto;
@ -49,7 +41,6 @@ export class ContentsPageComponent implements OnDestroy, OnInit {
public contentItems: ImmutableArray<ContentDto>; public contentItems: ImmutableArray<ContentDto>;
public contentFields: FieldDto[]; public contentFields: FieldDto[];
public contentsFilter = new FormControl();
public contentsQuery = ''; public contentsQuery = '';
public contentsPager = new Pager(0); public contentsPager = new Pager(0);
@ -70,58 +61,31 @@ export class ContentsPageComponent implements OnDestroy, OnInit {
public languageParameter: string; public languageParameter: string;
public isAllSelected = false; public isAllSelected = false;
public isReadOnly = false;
public isArchive = false; public isArchive = false;
constructor(public readonly ctx: AppContext, constructor(public readonly ctx: AppContext,
private readonly contentsService: ContentsService private readonly contentsService: ContentsService,
private readonly schemasState: SchemasState
) { ) {
} }
public ngOnDestroy() { public ngOnDestroy() {
this.contentCreatedSubscription.unsubscribe(); this.selectedSchemaSubscription.unsubscribe();
this.contentUpdatedSubscription.unsubscribe();
} }
public ngOnInit() { public ngOnInit() {
this.contentCreatedSubscription = this.selectedSchemaSubscription =
this.ctx.bus.of(ContentCreated) this.schemasState.selectedSchema
.subscribe(message => {
this.contentItems = this.contentItems.pushFront(message.content);
this.contentsPager = this.contentsPager.incrementCount();
});
this.contentUpdatedSubscription =
this.ctx.bus.of(ContentUpdated)
.subscribe(message => {
this.contentItems = this.contentItems.replaceBy('id', message.content, (o, n) => o.update(n.data, n.lastModifiedBy, n.version, n.lastModified));
});
const routeData = allData(this.ctx.route);
this.languages = routeData.appLanguages;
this.ctx.route.data.map(p => p.isReadOnly)
.subscribe(isReadOnly => {
this.isReadOnly = isReadOnly;
});
this.ctx.route.params.map(p => p.language)
.subscribe(language => {
this.languageSelected = this.languages.find(l => l.iso2Code === language) || this.languages.find(l => l.isMaster) || this.languages[0];
});
this.ctx.route.data.map(d => d.schema)
.subscribe(schema => { .subscribe(schema => {
this.schema = schema; this.schema = schema!;
this.resetContents(); this.resetContents();
this.load(); this.load();
}); });
}
public dropData(content: ContentDto) { const routeData = allData(this.ctx.route);
return { content, schemaId: this.schema.id };
this.languages = routeData.appLanguages;
} }
public publishContent(content: ContentDto) { public publishContent(content: ContentDto) {
@ -207,8 +171,6 @@ export class ContentsPageComponent implements OnDestroy, OnInit {
content = content.changeStatus(status, dt, this.ctx.userToken, dto.version); content = content.changeStatus(status, dt, this.ctx.userToken, dto.version);
this.contentItems = this.contentItems.replaceBy('id', content); this.contentItems = this.contentItems.replaceBy('id', content);
this.emitContentStatusChanged(content);
} }
}); });
} }
@ -233,9 +195,6 @@ export class ContentsPageComponent implements OnDestroy, OnInit {
public deleteContentItem(content: ContentDto): Observable<any> { public deleteContentItem(content: ContentDto): Observable<any> {
return this.contentsService.deleteContent(this.ctx.appName, this.schema.name, content.id, content.version) return this.contentsService.deleteContent(this.ctx.appName, this.schema.name, content.id, content.version)
.do(() => {
this.emitContentRemoved(content);
})
.catch(error => { .catch(error => {
this.ctx.notifyError(error); this.ctx.notifyError(error);
@ -247,8 +206,6 @@ export class ContentsPageComponent implements OnDestroy, OnInit {
content = content.update(update.payload, this.ctx.userToken, update.version); content = content.update(update.payload, this.ctx.userToken, update.version);
this.contentItems = this.contentItems.replaceBy('id', content); this.contentItems = this.contentItems.replaceBy('id', content);
this.emitContentUpdated(content);
} }
public load(showInfo = false) { public load(showInfo = false) {
@ -271,7 +228,6 @@ export class ContentsPageComponent implements OnDestroy, OnInit {
} }
public updateArchive(isArchive: boolean) { public updateArchive(isArchive: boolean) {
this.contentsQuery = this.contentsFilter.value;
this.contentsPager = new Pager(0); this.contentsPager = new Pager(0);
this.isArchive = isArchive; this.isArchive = isArchive;
@ -281,8 +237,8 @@ export class ContentsPageComponent implements OnDestroy, OnInit {
this.load(); this.load();
} }
public search() { public search(query: string) {
this.contentsQuery = this.contentsFilter.value; this.contentsQuery = query;
this.contentsPager = new Pager(0); this.contentsPager = new Pager(0);
this.load(); this.load();
@ -349,18 +305,6 @@ export class ContentsPageComponent implements OnDestroy, OnInit {
this.languageSelected = language; this.languageSelected = language;
} }
private emitContentStatusChanged(content: ContentDto) {
this.ctx.bus.emit(new ContentStatusChanged(content));
}
private emitContentUpdated(content: ContentDto) {
this.ctx.bus.emit(new ContentUpdated(content));
}
private emitContentRemoved(content: ContentDto) {
this.ctx.bus.emit(new ContentRemoved(content));
}
public trackByContent(content: ContentDto): string { public trackByContent(content: ContentDto): string {
return content.id; return content.id;
} }
@ -368,7 +312,6 @@ export class ContentsPageComponent implements OnDestroy, OnInit {
private resetContents() { private resetContents() {
this.contentItems = ImmutableArray.empty<ContentDto>(); this.contentItems = ImmutableArray.empty<ContentDto>();
this.contentsQuery = ''; this.contentsQuery = '';
this.contentsFilter.setValue('');
this.contentsPager = new Pager(0); this.contentsPager = new Pager(0);
this.selectedItems = {}; this.selectedItems = {};

24
src/Squidex/app/features/content/pages/contents/search-form.component.html

@ -1,4 +1,25 @@
<div class="form-horizontal"> <ng-container *ngIf="enableShortcut">
<sqx-shortcut keys="ctrl+shift+f" (trigger)="inputFind.focus()"></sqx-shortcut>
</ng-container>
<form class="form-inline search-form" (ngSubmit)="search()">
<input class="form-control form-control-expandable" #inputFind [formControl]="contentsFilter" placeholder="Search for content" />
<a class="expand-search" (click)="searchModal.toggle()" #archive>
<i class="icon-caret-down"></i>
</a>
</form>
<sqx-onboarding-tooltip id="contentArchive" [for]="archive" position="bottomRight" after="60000">
Click this icon to show the advanced search menu and to show the archive!
</sqx-onboarding-tooltip>
<sqx-onboarding-tooltip id="contentFind" [for]="inputFind" position="bottomRight" after="120000">
Search for content using full text search over all fields and languages!
</sqx-onboarding-tooltip>
<div class="dropdown-menu" *sqxModalView="searchModal" [sqxModalTarget]="inputFind">
<div class="form-horizontal">
<div [formGroup]="searchForm"> <div [formGroup]="searchForm">
<div class="form-group row"> <div class="form-group row">
<label class="col col-2 col-form-label" for="odataSearch">Text</label> <label class="col col-2 col-form-label" for="odataSearch">Text</label>
@ -35,4 +56,5 @@
<div class="link"> <div class="link">
Read more about filtering in the <a href="https://docs.squidex.io/04-guides/02-api.html" target="_blank">Documentation</a>. Read more about filtering in the <a href="https://docs.squidex.io/04-guides/02-api.html" target="_blank">Documentation</a>.
</div> </div>
</div>
</div> </div>

20
src/Squidex/app/features/content/pages/contents/search-form.component.scss

@ -1,6 +1,18 @@
@import '_vars'; @import '_vars';
@import '_mixins'; @import '_mixins';
.search-form {
display: inline-block;
}
.form-inline {
position: relative;
}
.form-control-expandable {
padding-right: 1.5rem;
}
.form-horizontal { .form-horizontal {
padding: 1rem 1.5rem; padding: 1rem 1.5rem;
min-width: 25rem; min-width: 25rem;
@ -14,6 +26,14 @@
text-align: right; text-align: right;
} }
.expand-search {
@include absolute(8px, 8px, auto, auto);
color: $color-border-dark !important;
font-size: .9rem;
font-weight: normal;
cursor: pointer !important;
}
.link { .link {
margin-top: 1.5rem; margin-top: 1.5rem;
font-size: .8rem; font-size: .8rem;

29
src/Squidex/app/features/content/pages/contents/search-form.component.ts

@ -6,7 +6,9 @@
*/ */
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output } from '@angular/core'; import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
import { FormBuilder } from '@angular/forms'; import { FormBuilder, FormControl } from '@angular/forms';
import { ModalView } from '@app/shared';
@Component({ @Component({
selector: 'sqx-search-form', selector: 'sqx-search-form',
@ -15,8 +17,6 @@ import { FormBuilder } from '@angular/forms';
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class SearchFormComponent implements OnChanges { export class SearchFormComponent implements OnChanges {
private queryValue = '';
@Input() @Input()
public query = ''; public query = '';
@ -32,6 +32,12 @@ export class SearchFormComponent implements OnChanges {
@Input() @Input()
public canArchive = true; public canArchive = true;
@Input()
public enableShortcut = false;
public contentsFilter = new FormControl();
public searchModal = new ModalView();
public searchForm = public searchForm =
this.formBuilder.group({ this.formBuilder.group({
odataOrderBy: '', odataOrderBy: '',
@ -44,8 +50,18 @@ export class SearchFormComponent implements OnChanges {
) { ) {
} }
public search() {
this.invalidate(this.contentsFilter.value);
this.queryChanged.emit(this.contentsFilter.value);
}
public ngOnChanges() { public ngOnChanges() {
if (this.query === this.queryValue) { this.invalidate(this.query);
}
private invalidate(query: string) {
if (query === this.contentsFilter.value) {
return; return;
} }
@ -81,7 +97,7 @@ export class SearchFormComponent implements OnChanges {
odataOrderBy odataOrderBy
}, { emitEvent: false }); }, { emitEvent: false });
this.queryValue = this.query; this.contentsFilter.setValue(this.query);
} }
public updateQuery() { public updateQuery() {
@ -112,8 +128,9 @@ export class SearchFormComponent implements OnChanges {
} }
if (query !== this.query) { if (query !== this.query) {
this.queryValue = query;
this.queryChanged.emit(query); this.queryChanged.emit(query);
} }
this.contentsFilter.setValue(query);
} }
} }

30
src/Squidex/app/features/content/pages/messages.ts

@ -5,39 +5,9 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
import { ContentDto } from '@app/shared';
export class ContentCreated {
constructor(
public readonly content: ContentDto
) {
}
}
export class ContentUpdated {
constructor(
public readonly content: ContentDto
) {
}
}
export class ContentRemoved {
constructor(
public readonly content: ContentDto
) {
}
}
export class ContentVersionSelected { export class ContentVersionSelected {
constructor( constructor(
public readonly version: number public readonly version: number
) { ) {
} }
} }
export class ContentStatusChanged {
constructor(
public readonly content: ContentDto
) {
}
}

8
src/Squidex/app/features/content/shared/contents-selector.component.html

@ -4,13 +4,7 @@
</ng-container> </ng-container>
<ng-container tabs> <ng-container tabs>
<form class="form-inline search-form" (ngSubmit)="search()"> <sqx-search-form (queryChanged)="search($event)"></sqx-search-form>
<input class="form-control form-control-expandable" [formControl]="contentsFilter" placeholder="Search for content" />
<a class="expand-search" (click)="searchModal.toggle()" #archive>
<i class="icon-caret-down"></i>
</a>
</form>
<button class="btn btn-link btn-secondary" (click)="load(true)"> <button class="btn btn-link btn-secondary" (click)="load(true)">
<i class="icon-reset"></i> Refresh <i class="icon-reset"></i> Refresh

28
src/Squidex/app/features/content/shared/contents-selector.component.scss

@ -1,34 +1,6 @@
@import '_vars'; @import '_vars';
@import '_mixins'; @import '_mixins';
.content {
cursor: pointer;
}
.icon-plus {
font-size: .8rem;
}
.search-form {
display: inline-block;
}
.form-inline {
position: relative;
}
.form-control-expandable {
padding-right: 1.5rem;
}
.expand-search {
@include absolute(8px, 8px, auto, auto);
color: $color-border-dark !important;
font-size: .9rem;
font-weight: normal;
cursor: pointer !important;
}
:host /deep/ .modal-body { :host /deep/ .modal-body {
background: $color-background; background: $color-background;
} }

6
src/Squidex/app/features/content/shared/contents-selector.component.ts

@ -6,7 +6,6 @@
*/ */
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { FormControl } from '@angular/forms';
import { import {
AppsState, AppsState,
@ -42,7 +41,6 @@ export class ContentsSelectorComponent implements OnInit {
public searchModal = new ModalView(); public searchModal = new ModalView();
public contentItems: ImmutableArray<ContentDto>; public contentItems: ImmutableArray<ContentDto>;
public contentsFilter = new FormControl();
public contentsQuery = ''; public contentsQuery = '';
public contentsPager = new Pager(0); public contentsPager = new Pager(0);
@ -81,8 +79,8 @@ export class ContentsSelectorComponent implements OnInit {
}); });
} }
public search() { public search(query: string) {
this.contentsQuery = this.contentsFilter.value; this.contentsQuery = query;
this.contentsPager = new Pager(0); this.contentsPager = new Pager(0);
this.load(); this.load();

2
src/Squidex/app/features/content/shared/references-editor.component.html

@ -1,5 +1,5 @@
<div class="references-container" [class.disabled]="isDisabled"> <div class="references-container" [class.disabled]="isDisabled">
<div class="drop-area-container" *ngIf="schema && !isDisabled"> <div class="drop-area-container" *ngIf="schema">
<div class="drop-area" (click)="selectorModal.show()"> <div class="drop-area" (click)="selectorModal.show()">
click here to link a content. click here to link a content.
</div> </div>

2
src/Squidex/app/features/schemas/declarations.ts

@ -5,8 +5,6 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
export * from './guards/schema-must-exist.guard';
export * from './pages/schema/types/assets-ui.component'; export * from './pages/schema/types/assets-ui.component';
export * from './pages/schema/types/assets-validation.component'; export * from './pages/schema/types/assets-validation.component';
export * from './pages/schema/types/boolean-ui.component'; export * from './pages/schema/types/boolean-ui.component';

2
src/Squidex/app/features/schemas/module.ts

@ -10,6 +10,7 @@ import { RouterModule, Routes } from '@angular/router';
import { DndModule } from 'ng2-dnd'; import { DndModule } from 'ng2-dnd';
import { import {
SchemaMustExistGuard,
SqxFrameworkModule, SqxFrameworkModule,
SqxSharedModule SqxSharedModule
} from '@app/shared'; } from '@app/shared';
@ -36,7 +37,6 @@ import {
ReferencesValidationComponent, ReferencesValidationComponent,
SchemaEditFormComponent, SchemaEditFormComponent,
SchemaFormComponent, SchemaFormComponent,
SchemaMustExistGuard,
SchemaPageComponent, SchemaPageComponent,
SchemasPageComponent, SchemasPageComponent,
SchemaScriptsFormComponent, SchemaScriptsFormComponent,

38
src/Squidex/app/shared/guards/schema-must-exist-published.guard.ts

@ -0,0 +1,38 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { allParams } from '@app/framework';
import { SchemasState } from './../state/schemas.state';
@Injectable()
export class SchemaMustExistPublishedGuard implements CanActivate {
constructor(
private readonly schemasState: SchemasState,
private readonly router: Router
) {
}
public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
const schemaName = allParams(route)['schemaName'];
const result =
this.schemasState.selectSchema(schemaName)
.do(dto => {
if (!dto || !dto.isPublished) {
this.router.navigate(['/404']);
}
})
.map(s => s !== null && s.isPublished);
return result;
}
}

6
src/Squidex/app/features/schemas/guards/schema-must-exist.guard.ts → src/Squidex/app/shared/guards/schema-must-exist.guard.ts

@ -9,7 +9,9 @@ import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { allParams, SchemasState } from '@app/shared'; import { allParams } from '@app/framework';
import { SchemasState } from './../state/schemas.state';
@Injectable() @Injectable()
export class SchemaMustExistGuard implements CanActivate { export class SchemaMustExistGuard implements CanActivate {
@ -29,7 +31,7 @@ export class SchemaMustExistGuard implements CanActivate {
this.router.navigate(['/404']); this.router.navigate(['/404']);
} }
}) })
.map(u => u !== null); .map(s => s !== null);
return result; return result;
} }

2
src/Squidex/app/shared/internal.ts

@ -13,6 +13,8 @@ export * from './guards/resolve-app-languages.guard';
export * from './guards/resolve-content.guard'; export * from './guards/resolve-content.guard';
export * from './guards/resolve-published-schema.guard'; export * from './guards/resolve-published-schema.guard';
export * from './guards/resolve-schema.guard'; export * from './guards/resolve-schema.guard';
export * from './guards/schema-must-exist-published.guard';
export * from './guards/schema-must-exist.guard';
export * from './guards/unset-app.guard'; export * from './guards/unset-app.guard';
export * from './interceptors/auth.interceptor'; export * from './interceptors/auth.interceptor';

4
src/Squidex/app/shared/module.ts

@ -51,6 +51,8 @@ import {
ResolveContentGuard, ResolveContentGuard,
ResolvePublishedSchemaGuard, ResolvePublishedSchemaGuard,
ResolveSchemaGuard, ResolveSchemaGuard,
SchemaMustExistGuard,
SchemaMustExistPublishedGuard,
SchemasService, SchemasService,
RulesService, RulesService,
UIService, UIService,
@ -151,6 +153,8 @@ export class SqxSharedModule {
ResolvePublishedSchemaGuard, ResolvePublishedSchemaGuard,
ResolveSchemaGuard, ResolveSchemaGuard,
RulesService, RulesService,
SchemaMustExistGuard,
SchemaMustExistPublishedGuard,
SchemasService, SchemasService,
SchemasState, SchemasState,
UIService, UIService,

Loading…
Cancel
Save