diff --git a/src/Squidex.Write/Schemas/Commands/AddField.cs b/src/Squidex.Write/Schemas/Commands/AddField.cs index 29a1aa885..baddf89a1 100644 --- a/src/Squidex.Write/Schemas/Commands/AddField.cs +++ b/src/Squidex.Write/Schemas/Commands/AddField.cs @@ -25,12 +25,12 @@ namespace Squidex.Write.Schemas.Commands { if (!Partitioning.IsValidPartitioning()) { - errors.Add(new ValidationError($"Partitioning is not valid.", nameof(Partitioning))); + errors.Add(new ValidationError("Partitioning is not valid.", nameof(Partitioning))); } if (!Name.IsPropertyName()) { - errors.Add(new ValidationError("Name must be a valid property name", nameof(Name))); + errors.Add(new ValidationError("Name must be a valid property name.", nameof(Name))); } if (Properties == null) diff --git a/src/Squidex/Controllers/Api/History/HistoryController.cs b/src/Squidex/Controllers/Api/History/HistoryController.cs index 8183c54f1..1950a8dd9 100644 --- a/src/Squidex/Controllers/Api/History/HistoryController.cs +++ b/src/Squidex/Controllers/Api/History/HistoryController.cs @@ -15,7 +15,6 @@ using Squidex.Controllers.Api.History.Models; using Squidex.Infrastructure.CQRS.Commands; using Squidex.Infrastructure.Reflection; using Squidex.Pipeline; -using Squidex.Read.Apps.Services; using Squidex.Read.History.Repositories; namespace Squidex.Controllers.Api.History @@ -23,7 +22,7 @@ namespace Squidex.Controllers.Api.History /// /// Readonly API to get an event stream. /// - [Authorize] + [MustBeAppEditor] [ApiExceptionFilter] [AppApi] [SwaggerTag("History")] diff --git a/src/Squidex/Controllers/Api/Schemas/SchemasController.cs b/src/Squidex/Controllers/Api/Schemas/SchemasController.cs index 4aee89835..504a17770 100644 --- a/src/Squidex/Controllers/Api/Schemas/SchemasController.cs +++ b/src/Squidex/Controllers/Api/Schemas/SchemasController.cs @@ -25,7 +25,6 @@ namespace Squidex.Controllers.Api.Schemas /// /// Manages and retrieves information about schemas. /// - [MustBeAppDeveloper] [ApiExceptionFilter] [AppApi] [SwaggerTag("Schemas")] @@ -47,6 +46,7 @@ namespace Squidex.Controllers.Api.Schemas /// 200 => Schemas returned. /// 404 => App not found. /// + [MustBeAppEditor] [HttpGet] [Route("apps/{app}/schemas/")] [ProducesResponseType(typeof(SchemaDto[]), 200)] @@ -69,6 +69,7 @@ namespace Squidex.Controllers.Api.Schemas /// 200 => Schema found. /// 404 => Schema or app not found. /// + [MustBeAppEditor] [HttpGet] [Route("apps/{app}/schemas/{name}/")] [ProducesResponseType(typeof(SchemaDetailsDto[]), 200)] @@ -99,6 +100,7 @@ namespace Squidex.Controllers.Api.Schemas /// 400 => Schema name or properties are not valid. /// 409 => Schema name already in use. /// + [MustBeAppDeveloper] [HttpPost] [Route("apps/{app}/schemas/")] [ProducesResponseType(typeof(EntityCreatedDto), 201)] @@ -125,6 +127,7 @@ namespace Squidex.Controllers.Api.Schemas /// 400 => Schema properties are not valid. /// 404 => Schema or app not found. /// + [MustBeAppDeveloper] [HttpPut] [Route("apps/{app}/schemas/{name}/")] [ApiCosts(1)] @@ -147,6 +150,7 @@ namespace Squidex.Controllers.Api.Schemas /// 400 => Schema is already published. /// 404 => Schema or app not found. /// + [MustBeAppDeveloper] [HttpPut] [Route("apps/{app}/schemas/{name}/publish")] [ProducesResponseType(typeof(ErrorDto), 400)] @@ -168,6 +172,7 @@ namespace Squidex.Controllers.Api.Schemas /// 400 => Schema is not published. /// 404 => Schema or app not found. /// + [MustBeAppDeveloper] [HttpPut] [Route("apps/{app}/schemas/{name}/unpublish")] [ProducesResponseType(typeof(ErrorDto), 400)] @@ -188,6 +193,7 @@ namespace Squidex.Controllers.Api.Schemas /// 204 => Schema has been deleted. /// 404 => Schema or app not found. /// + [MustBeAppDeveloper] [HttpDelete] [Route("apps/{app}/schemas/{name}/")] [ApiCosts(1)] diff --git a/src/Squidex/Controllers/Api/Statistics/UsagesController.cs b/src/Squidex/Controllers/Api/Statistics/UsagesController.cs index 0dd817aac..6816be54b 100644 --- a/src/Squidex/Controllers/Api/Statistics/UsagesController.cs +++ b/src/Squidex/Controllers/Api/Statistics/UsagesController.cs @@ -23,6 +23,7 @@ namespace Squidex.Controllers.Api.Statistics /// /// Retrieves usage information for apps. /// + [MustBeAppEditor] [ApiExceptionFilter] [AppApi] [SwaggerTag("Statistics")] @@ -53,7 +54,6 @@ namespace Squidex.Controllers.Api.Statistics /// 200 => Usage tracking results returned. /// 404 => App not found. /// - [MustBeAppEditor] [HttpGet] [Route("apps/{app}/usages/calls/month")] [ProducesResponseType(typeof(CurrentCallsDto), 200)] @@ -78,7 +78,6 @@ namespace Squidex.Controllers.Api.Statistics /// 404 => App not found. /// 400 => Range between from date and to date is not valid or has more than 100 days. /// - [MustBeAppEditor] [HttpGet] [Route("apps/{app}/usages/calls/{fromDate}/{toDate}")] [ProducesResponseType(typeof(CallsUsageDto[]), 200)] @@ -110,7 +109,6 @@ namespace Squidex.Controllers.Api.Statistics /// 200 => Storage usage returned. /// 404 => App not found. /// - [MustBeAppEditor] [HttpGet] [Route("apps/{app}/usages/storage/today")] [ProducesResponseType(typeof(CurrentStorageDto), 200)] @@ -135,7 +133,6 @@ namespace Squidex.Controllers.Api.Statistics /// 404 => App not found. /// 400 => Range between from date and to date is not valid or has more than 100 days. /// - [MustBeAppEditor] [HttpGet] [Route("apps/{app}/usages/storage/{fromDate}/{toDate}")] [ProducesResponseType(typeof(StorageUsageDto[]), 200)] diff --git a/src/Squidex/Pipeline/AppApiFilter.cs b/src/Squidex/Pipeline/AppApiFilter.cs index cebbb53c0..9386e104e 100644 --- a/src/Squidex/Pipeline/AppApiFilter.cs +++ b/src/Squidex/Pipeline/AppApiFilter.cs @@ -82,11 +82,11 @@ namespace Squidex.Pipeline defaultIdentity.AddClaim(new Claim(defaultIdentity.RoleClaimType, SquidexRoles.AppDeveloper)); defaultIdentity.AddClaim(new Claim(defaultIdentity.RoleClaimType, SquidexRoles.AppEditor)); break; - case PermissionLevel.Editor: + case PermissionLevel.Developer: defaultIdentity.AddClaim(new Claim(defaultIdentity.RoleClaimType, SquidexRoles.AppDeveloper)); defaultIdentity.AddClaim(new Claim(defaultIdentity.RoleClaimType, SquidexRoles.AppEditor)); break; - case PermissionLevel.Developer: + case PermissionLevel.Editor: defaultIdentity.AddClaim(new Claim(defaultIdentity.RoleClaimType, SquidexRoles.AppEditor)); break; } diff --git a/src/Squidex/app/features/administration/pages/event-consumers/event-consumers-page.component.html b/src/Squidex/app/features/administration/pages/event-consumers/event-consumers-page.component.html index 59452d34e..7bb7701fd 100644 --- a/src/Squidex/app/features/administration/pages/event-consumers/event-consumers-page.component.html +++ b/src/Squidex/app/features/administration/pages/event-consumers/event-consumers-page.component.html @@ -6,7 +6,7 @@

Event Consumers

- + diff --git a/src/Squidex/app/features/administration/pages/users/users-page.component.html b/src/Squidex/app/features/administration/pages/users/users-page.component.html index 5f0b81c4a..211cf99ed 100644 --- a/src/Squidex/app/features/administration/pages/users/users-page.component.html +++ b/src/Squidex/app/features/administration/pages/users/users-page.component.html @@ -18,7 +18,7 @@

Users

- + diff --git a/src/Squidex/app/features/assets/pages/assets-page.component.html b/src/Squidex/app/features/assets/pages/assets-page.component.html index c20cc9755..a56dc0230 100644 --- a/src/Squidex/app/features/assets/pages/assets-page.component.html +++ b/src/Squidex/app/features/assets/pages/assets-page.component.html @@ -18,7 +18,7 @@

Assets

- + diff --git a/src/Squidex/app/features/content/pages/content/content-page.component.ts b/src/Squidex/app/features/content/pages/content/content-page.component.ts index 177dbcc45..6297e22c3 100644 --- a/src/Squidex/app/features/content/pages/content/content-page.component.ts +++ b/src/Squidex/app/features/content/pages/content/content-page.component.ts @@ -6,7 +6,6 @@ */ import { Component, OnDestroy, OnInit } from '@angular/core'; -import { Location } from '@angular/common'; import { AbstractControl, FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; import { Observable, Subject, Subscription } from 'rxjs'; @@ -62,7 +61,6 @@ export class ContentPageComponent extends AppComponentBase implements CanCompone constructor(apps: AppsStoreService, notifications: NotificationService, private readonly contentsService: ContentsService, - private readonly location: Location, private readonly route: ActivatedRoute, private readonly router: Router, private readonly messageBus: MessageBus @@ -151,10 +149,8 @@ export class ContentPageComponent extends AppComponentBase implements CanCompone this.messageBus.publish(new ContentCreated(created.id, created.data, this.version.value, publish)); - this.finishCreation(); - this.notifyInfo('Content created successfully.'); - this.enable(); + this.finish(); }, error => { this.notifyError(error); this.enable(); @@ -177,11 +173,8 @@ export class ContentPageComponent extends AppComponentBase implements CanCompone } } - private finishCreation() { - const newUrl = this.router.createUrlTree(['../', this.contentId], { relativeTo: this.route, replaceUrl: true }); - - this.location.replaceState(newUrl.toString()); - this.isNewMode = false; + private finish() { + this.router.navigate(['../'], { relativeTo: this.route, replaceUrl: true }); } private enable() { diff --git a/src/Squidex/app/features/content/pages/contents/content-item.component.ts b/src/Squidex/app/features/content/pages/contents/content-item.component.ts index a4a8c9bfa..6a7a8c300 100644 --- a/src/Squidex/app/features/content/pages/contents/content-item.component.ts +++ b/src/Squidex/app/features/content/pages/contents/content-item.component.ts @@ -93,12 +93,18 @@ export class ContentItemComponent extends AppComponentBase implements OnInit, On if (value) { if (properties.fieldType === 'Json') { - value = 'Json'; + value = ''; } else if (properties.fieldType === 'Geolocation') { value = `${value.longitude}, ${value.latitude}`; } else if (properties.fieldType === 'Boolean') { value = value ? '✔' : '-'; - } else if (properties.fieldType === 'DateTime') { + }else if (properties.fieldType === 'Assets') { + try { + value = `${value.length} Assets`; + } catch (ex) { + value = '0 Assets'; + } + } else if (properties.fieldType === 'DateTime') { try { const parsed = DateTime.parseISO_UTC(value); diff --git a/src/Squidex/app/features/content/pages/contents/contents-page.component.html b/src/Squidex/app/features/content/pages/contents/contents-page.component.html index 22c899b07..d10f2be6b 100644 --- a/src/Squidex/app/features/content/pages/contents/contents-page.component.html +++ b/src/Squidex/app/features/content/pages/contents/contents-page.component.html @@ -18,7 +18,7 @@ - diff --git a/src/Squidex/app/features/content/pages/contents/contents-page.component.ts b/src/Squidex/app/features/content/pages/contents/contents-page.component.ts index 96fd3480b..f748035ef 100644 --- a/src/Squidex/app/features/content/pages/contents/contents-page.component.ts +++ b/src/Squidex/app/features/content/pages/contents/contents-page.component.ts @@ -7,7 +7,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { FormControl } from '@angular/forms'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { Subscription } from 'rxjs'; import { @@ -59,6 +59,7 @@ export class ContentsPageComponent extends AppComponentBase implements OnDestroy private readonly authService: AuthService, private readonly contentsService: ContentsService, private readonly route: ActivatedRoute, + private readonly router: Router, private readonly messageBus: MessageBus ) { super(notifications, apps); @@ -187,6 +188,10 @@ export class ContentsPageComponent extends AppComponentBase implements OnDestroy this.load(); } + public gotoNew() { + this.router.navigate(['./new'], { relativeTo: this.route }); + } + private updateContents(id: string, p: boolean | undefined, data: any, version: string) { this.contentItems = this.contentItems.replaceAll(x => x.id === id, c => this.updateContent(c, p === undefined ? c.isPublished : p, data, version)); } diff --git a/src/Squidex/app/features/content/pages/schemas/schemas-page.component.html b/src/Squidex/app/features/content/pages/schemas/schemas-page.component.html index cdedec251..a84f0cc89 100644 --- a/src/Squidex/app/features/content/pages/schemas/schemas-page.component.html +++ b/src/Squidex/app/features/content/pages/schemas/schemas-page.component.html @@ -6,7 +6,7 @@

Schemas

- + diff --git a/src/Squidex/app/features/schemas/pages/schemas/schemas-page.component.html b/src/Squidex/app/features/schemas/pages/schemas/schemas-page.component.html index 390ed6ece..66958ec70 100644 --- a/src/Squidex/app/features/schemas/pages/schemas/schemas-page.component.html +++ b/src/Squidex/app/features/schemas/pages/schemas/schemas-page.component.html @@ -6,7 +6,7 @@

Schemas

- + diff --git a/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts b/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts index 2af4df749..43b2aeb43 100644 --- a/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts +++ b/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts @@ -74,7 +74,7 @@ export class ContributorsPageComponent extends AppComponentBase implements OnIni ]; public get canAddContributor() { - return this.addContributorForm.valid && (this.maxContributors < -1 || this.appContributors.length < this.maxContributors); + return this.addContributorForm.valid && (this.maxContributors <= -1 || this.appContributors.length < this.maxContributors); } public addContributorForm: FormGroup = diff --git a/src/Squidex/app/features/settings/settings-area.component.html b/src/Squidex/app/features/settings/settings-area.component.html index 8675c0256..f2de9c72f 100644 --- a/src/Squidex/app/features/settings/settings-area.component.html +++ b/src/Squidex/app/features/settings/settings-area.component.html @@ -6,7 +6,7 @@

Settings

- + diff --git a/src/Squidex/app/framework/angular/parent-link.directive.ts b/src/Squidex/app/framework/angular/parent-link.directive.ts index f09a398d9..ea69384d2 100644 --- a/src/Squidex/app/framework/angular/parent-link.directive.ts +++ b/src/Squidex/app/framework/angular/parent-link.directive.ts @@ -5,7 +5,7 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ -import { Directive, ElementRef, HostListener, OnDestroy, OnInit, Renderer } from '@angular/core'; +import { Directive, ElementRef, HostListener, Input, OnDestroy, OnInit, Renderer } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { Subscription } from 'rxjs'; @@ -16,6 +16,9 @@ export class ParentLinkDirective implements OnInit, OnDestroy { private urlSubscription: Subscription; private url: string; + @Input() + public isLazyLoaded = false; + constructor( private readonly router: Router, private readonly route: ActivatedRoute, @@ -31,7 +34,9 @@ export class ParentLinkDirective implements OnInit, OnDestroy { public ngOnInit() { this.urlSubscription = this.route.url.subscribe(() => { - this.url = this.router.createUrlTree(['.'], { relativeTo: this.route.parent.parent }).toString(); + this.url = this.isLazyLoaded ? + this.router.createUrlTree(['.'], { relativeTo: this.route.parent.parent }).toString() : + this.router.createUrlTree(['.'], { relativeTo: this.route.parent }).toString(); this.renderer.setElementAttribute(this.element.nativeElement, 'href', this.url); }); diff --git a/src/Squidex/app/shared/services/auth.service.ts b/src/Squidex/app/shared/services/auth.service.ts index 7d9c58329..214b735d0 100644 --- a/src/Squidex/app/shared/services/auth.service.ts +++ b/src/Squidex/app/shared/services/auth.service.ts @@ -203,16 +203,14 @@ export class AuthService { } }) .catch((error: Response) => { - if (error.status >= 401 && error.status <= 404 && (!this.user || this.user.user.expired)) { + if (error.status === 404 && (!this.user || this.user.user.expired)) { this.logoutRedirect(); return Observable.empty(); - } else if (error.status === 401) { + } else if (error.status === 401 || error.status === 403) { this.logoutRedirect(); return Observable.empty(); - } else if (error.status === 403) { - error.status = 404; } return Observable.throw(error); });