Browse Source

Inline editor for references.

pull/502/head
Sebastian 6 years ago
parent
commit
eb78dc66ed
  1. 7
      backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDto.cs
  2. 1
      frontend/app/features/content/declarations.ts
  3. 2
      frontend/app/features/content/module.ts
  4. 12
      frontend/app/features/content/pages/content/content-field.component.scss
  5. 8
      frontend/app/features/content/pages/content/content-page.component.html
  6. 4
      frontend/app/features/content/pages/content/content-page.component.ts
  7. 56
      frontend/app/features/content/shared/references/content-creator.component.html
  8. 11
      frontend/app/features/content/shared/references/content-creator.component.scss
  9. 141
      frontend/app/features/content/shared/references/content-creator.component.ts
  10. 12
      frontend/app/features/content/shared/references/content-selector.component.html
  11. 25
      frontend/app/features/content/shared/references/references-editor.component.html
  12. 6
      frontend/app/features/content/shared/references/references-editor.component.scss
  13. 6
      frontend/app/features/content/shared/references/references-editor.component.ts
  14. 10
      frontend/app/features/schemas/pages/schema/fields/field-wizard.component.html
  15. 6
      frontend/app/shared/services/schemas.service.ts

7
backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDto.cs

@ -105,6 +105,13 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
AddGetLink("contents", controller.Url<ContentsController>(x => nameof(x.GetContents), values));
}
if (controller.HasPermission(Permissions.AppContentsCreate, app, Name))
{
AddPostLink("contents/create", controller.Url<ContentsController>(x => nameof(x.PostContent), values));
AddPostLink("contents/create/publish", controller.Url<ContentsController>(x => nameof(x.PostContent), values) + "?publish=true");
}
if (controller.HasPermission(Permissions.AppSchemasPublish, app, Name))
{
if (IsPublished)

1
frontend/app/features/content/declarations.ts

@ -35,5 +35,6 @@ export * from './shared/list/content.component';
export * from './shared/references/content-selector-item.component';
export * from './shared/references/content-selector.component';
export * from './shared/references/content-creator.component';
export * from './shared/references/reference-item.component';
export * from './shared/references/references-editor.component';

2
frontend/app/features/content/module.ts

@ -25,6 +25,7 @@ import {
AssetsEditorComponent,
CommentsPageComponent,
ContentComponent,
ContentCreatorComponent,
ContentEventComponent,
ContentFieldComponent,
ContentHistoryPageComponent,
@ -118,6 +119,7 @@ const routes: Routes = [
AssetsEditorComponent,
CommentsPageComponent,
ContentComponent,
ContentCreatorComponent,
ContentEventComponent,
ContentFieldComponent,
ContentHistoryPageComponent,

12
frontend/app/features/content/pages/content/content-field.component.scss

@ -7,25 +7,13 @@
@include absolute(.25rem, 1.25rem);
}
.row {
margin-left: -1.5rem;
margin-right: -1.5rem;
}
.col-12 {
padding-left: 1.5rem;
padding-right: 1.5rem;
}
.col-6 {
& {
padding-left: 1.5rem;
padding-right: .5rem;
}
&.col-right {
padding-left: .5rem;
padding-right: 1.5rem;
}
}

8
frontend/app/features/content/pages/content/content-page.component.html

@ -82,14 +82,14 @@
<div content>
<sqx-content-field *ngFor="let field of schema.fields; trackBy: trackByField"
[form]="contentForm"
[formContext]="formContext"
[(language)]="language"
[field]="field"
[fieldForm]="contentForm.form.get(field.name)"
[fieldFormCompare]="contentFormCompare?.form.get(field.name)"
[schema]="schema"
[form]="contentForm"
[formContext]="formContext"
[languages]="languages"
[(language)]="language">
[schema]="schema">
</sqx-content-field>
</div>
</sqx-list-view>

4
frontend/app/features/content/pages/content/content-page.component.ts

@ -150,10 +150,6 @@ export class ContentPageComponent extends ResourceOwner implements CanComponentD
this.saveContent(true);
}
public saveAsDraft() {
this.saveContent(false);
}
public save() {
this.saveContent(false);
}

56
frontend/app/features/content/shared/references/content-creator.component.html

@ -0,0 +1,56 @@
<sqx-modal-dialog (close)="emitComplete()" size="lg" [showFooter]="false">
<ng-container title>
<div class="row">
<div class="col-selector">
<select class="form-control form-control-dark"
[ngModel]="schema?.id"
(ngModelChange)="selectSchema($event)">
<option *ngFor="let schema of schemas" [ngValue]="schema.id">
Select {{schema.displayName}}
</option>
</select>
</div>
</div>
</ng-container>
<ng-container tabs>
<div class="row no-gutters">
<div class="col-auto">
<div *ngIf="schema && languages.length > 1">
<sqx-language-selector class="languages-buttons"
(selectedLanguageChange)="selectLanguage($event)" [languages]="languages">
</sqx-language-selector>
</div>
</div>
<div class="col text-right">
<button type="button" class="btn btn-outline-success" (click)="save()">
Create
</button>
<button type="button" class="btn btn-success ml-1" (click)="saveAndPublish()" *ngIf="schema?.canContentsCreateAndPublish">
Create and Publish
</button>
<sqx-form-error bubble="true" closeable="true" [error]="contentForm?.error | async"></sqx-form-error>
</div>
</div>
</ng-container>
<ng-container content>
<ng-container *ngIf="schema && contentForm">
<form [formGroup]="contentForm.form" (ngSubmit)="saveAndPublish()">
<sqx-content-field *ngFor="let field of schema.fields"
(languageChange)="selectLanguage($event)"
[field]="field"
[fieldForm]="contentForm.form.get(field.name)"
[form]="contentForm"
[formContext]="contentFormContext"
[language]="language"
[languages]="languages"
[schema]="schema">
</sqx-content-field>
</form>
</ng-container>
</ng-container>
</sqx-modal-dialog>

11
frontend/app/features/content/shared/references/content-creator.component.scss

@ -0,0 +1,11 @@
:host ::ng-deep {
.modal-tabs {
background: $color-white;
padding-left: 1.75rem;
padding-right: 1.75rem;
}
.modal-content {
background: $color-background;
}
}

141
frontend/app/features/content/shared/references/content-creator.component.ts

@ -0,0 +1,141 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import {
ApiUrlConfig,
AppLanguageDto,
AppsState,
AuthService,
ContentDto,
EditContentForm,
LanguageDto,
ManualContentsState,
ResourceOwner,
SchemaDetailsDto,
SchemaDto,
SchemasState,
Types
} from '@app/shared';
@Component({
selector: 'sqx-content-creator',
styleUrls: ['./content-creator.component.scss'],
templateUrl: './content-creator.component.html',
providers: [
ManualContentsState
]
})
export class ContentCreatorComponent extends ResourceOwner implements OnInit {
@Output()
public select = new EventEmitter<ReadonlyArray<ContentDto>>();
@Input()
public schemaIds: ReadonlyArray<string>;
@Input()
public language: LanguageDto;
@Input()
public languages: ReadonlyArray<AppLanguageDto>;
public schema: SchemaDetailsDto;
public schemas: ReadonlyArray<SchemaDto> = [];
public contentFormContext: any;
public contentForm: EditContentForm;
constructor(authService: AuthService,
public readonly appsState: AppsState,
public readonly apiUrl: ApiUrlConfig,
public readonly contentsState: ManualContentsState,
public readonly schemasState: SchemasState,
private readonly changeDetector: ChangeDetectorRef
) {
super();
this.contentFormContext = { user: authService.user, apiUrl: apiUrl.buildUrl('api') };
}
public ngOnInit() {
this.schemas = this.schemasState.snapshot.schemas.filter(x => x.canContentsCreate);
if (this.schemaIds && this.schemaIds.length > 0) {
this.schemas = this.schemas.filter(x => this.schemaIds.indexOf(x.id) >= 0);
}
this.selectSchema(this.schemas[0]);
}
public selectSchema(selected: string | SchemaDto) {
if (Types.is(selected, SchemaDto)) {
selected = selected.id;
}
this.schemasState.loadSchema(selected, true)
.subscribe(schema => {
if (schema) {
this.schema = schema;
this.contentsState.schema = schema;
this.contentForm = new EditContentForm(this.languages, this.schema);
this.changeDetector.markForCheck();
}
});
}
public saveAndPublish() {
this.saveContent(true);
}
public save() {
this.saveContent(false);
}
private saveContent(publish: boolean) {
const value = this.contentForm.submit();
if (value) {
if (!this.canCreate(publish)) {
return;
}
this.contentsState.create(value, publish)
.subscribe(content => {
this.contentForm.submitCompleted({ noReset: true });
this.emitSelect(content);
}, error => {
this.contentForm.submitFailed(error);
});
} else {
this.contentForm.submitFailed('Content element not valid, please check the field with the red bar on the left in all languages (if localizable).');
}
}
private canCreate(publish: boolean) {
if (publish) {
return this.schema.canContentsCreateAndPublish;
} else {
return this.schema.canContentsCreateAndPublish;
}
}
public emitComplete() {
this.select.emit([]);
}
public emitSelect(content: ContentDto) {
this.select.emit([content]);
}
public selectLanguage(language: LanguageDto) {
this.language = language;
}
}

12
frontend/app/features/content/shared/references/content-selector.component.html

@ -29,14 +29,10 @@
</sqx-search-form>
</div>
<div class="-auto pl-1" *ngIf="languages.length > 1">
<sqx-language-selector class="languages-buttons" (selectedLanguageChange)="selectLanguage($event)" [languages]="languages"></sqx-language-selector>
</div>
<div class="-auto pl-1">
<a class="btn btn-success" [routerLink]="['../..', schema.name]" target="_blank">
<i class="icon-plus"></i>
</a>
<div class="col-auto pl-1" *ngIf="languages.length > 1">
<sqx-language-selector class="languages-buttons"
(selectedLanguageChange)="selectLanguage($event)" [languages]="languages">
</sqx-language-selector>
</div>
</ng-container>
</div>

25
frontend/app/features/content/shared/references/references-editor.component.html

@ -4,8 +4,12 @@
[sqxResizeMaxWidth]="0">
<ng-container>
<div class="drop-area-container">
<div class="drop-area" (click)="selectorDialog.show()">
Click here to link content items.
<div class="drop-area">
<a (click)="contentCreatorDialog.show()">Add New</a>
&middot;
<a (click)="contentSelectorDialog.show()">Select Existing</a>
</div>
</div>
@ -19,10 +23,10 @@
class="table-drag"
cdkDrag
cdkDragLockAxis="y"
[language]="language"
[columns]="snapshot.columns"
[isCompact]="snapshot.isCompact"
[isDisabled]="snapshot.isDisabled"
[language]="language"
(delete)="remove(content)">
<i cdkDragHandle class="icon-drag2 drag-handle"></i>
</tbody>
@ -30,13 +34,22 @@
</ng-container>
</div>
<ng-container *sqxModal="selectorDialog">
<ng-container *sqxModal="contentCreatorDialog">
<sqx-content-creator
(select)="select($event)"
[language]="language"
[languages]="languages"
[schemaIds]="schemaIds">
</sqx-content-creator>
</ng-container>
<ng-container *sqxModal="contentSelectorDialog">
<sqx-content-selector
(select)="select($event)"
[allowDuplicates]="allowDuplicates"
[alreadySelected]="snapshot.contentItems"
[language]="language"
[languages]="languages"
[schemaIds]="schemaIds"
(select)="select($event)">
[schemaIds]="schemaIds">
</sqx-content-selector>
</ng-container>

6
frontend/app/features/content/shared/references/references-editor.component.scss

@ -32,8 +32,10 @@
transition: border-color .4s ease;
}
&:hover {
text-decoration: underline;
a {
&:hover {
text-decoration: underline;
}
}
}

6
frontend/app/features/content/shared/references/references-editor.component.ts

@ -57,7 +57,8 @@ export class ReferencesEditorComponent extends StatefulControlComponent<State, R
@Input()
public allowDuplicates = true;
public selectorDialog = new DialogModel();
public contentCreatorDialog = new DialogModel();
public contentSelectorDialog = new DialogModel();
constructor(changeDetector: ChangeDetectorRef,
private readonly appsState: AppsState,
@ -104,7 +105,8 @@ export class ReferencesEditorComponent extends StatefulControlComponent<State, R
this.updateValue();
}
this.selectorDialog.hide();
this.contentSelectorDialog.hide();
this.contentCreatorDialog.hide();
}
public remove(content: ContentDto) {

10
frontend/app/features/schemas/pages/schema/fields/field-wizard.component.html

@ -79,14 +79,14 @@
<button type="reset" class="float-left btn btn-secondary" (click)="emitComplete()">Cancel</button>
<div class="float-right" *ngIf="!editing">
<button type="button" class="btn btn-outline-success mr-1" (click)="addField(false)">Create and close</button>
<button type="button" class="btn btn-success mr-1" (click)="addField(true)">Create and add field</button>
<button type="button" class="btn btn-success" (click)="addField(false, true)">Create and edit field</button>
<button type="button" class="btn btn-outline-success" (click)="addField(false)">Create and close</button>
<button type="button" class="btn btn-success ml-1" (click)="addField(true)">Create and add field</button>
<button type="button" class="btn btn-success ml-1" (click)="addField(false, true)">Create and edit field</button>
</div>
<div class="float-right" *ngIf="editing">
<button type="button" class="btn btn-success mr-1" (click)="save(true)">Save and add field</button>
<button type="button" class="btn btn-primary" (click)="save()">Save and close</button>
<button type="button" class="btn btn-success" (click)="save(true)">Save and add field</button>
<button type="button" class="btn btn-primary ml-1" (click)="save()">Save and close</button>
</div>
</ng-container>
</sqx-modal-dialog>

6
frontend/app/shared/services/schemas.service.ts

@ -55,6 +55,9 @@ export class SchemaDto {
public readonly _links: ResourceLinks;
public readonly canAddField: boolean;
public readonly canContentsRead: boolean;
public readonly canContentsCreate: boolean;
public readonly canContentsCreateAndPublish: boolean;
public readonly canDelete: boolean;
public readonly canOrderFields: boolean;
public readonly canPublish: boolean;
@ -85,6 +88,9 @@ export class SchemaDto {
this._links = links;
this.canAddField = hasAnyLink(links, 'fields/add');
this.canContentsRead = hasAnyLink(links, 'contents');
this.canContentsCreate = hasAnyLink(links, 'contents/create');
this.canContentsCreateAndPublish = hasAnyLink(links, 'contents/create/publish');
this.canDelete = hasAnyLink(links, 'delete');
this.canOrderFields = hasAnyLink(links, 'fields/order');
this.canPublish = hasAnyLink(links, 'publish');

Loading…
Cancel
Save