Browse Source

Merge branch 'master' into orleans

# Conflicts:
#	src/Squidex.Domain.Apps.Core.Model/Apps/Json/JsonLanguagesConfig.cs
#	src/Squidex.Infrastructure/Log/Adapter/SemanticLogLoggerFactoryExtensions.cs
pull/169/head
Sebastian Stehle 9 years ago
parent
commit
b2a6dcecdf
  1. 1
      .drone.yml
  2. 2
      src/Squidex.Domain.Apps.Core.Model/Schemas/Schema.cs
  3. 4
      src/Squidex.Domain.Apps.Read/Contents/GraphQL/GraphQLModel.cs
  4. 3
      src/Squidex/Controllers/Api/Ping/PingController.cs
  5. 14
      src/Squidex/Squidex.csproj
  6. 6
      src/Squidex/app/features/api/pages/graphql/graphql-page.component.scss
  7. 2
      src/Squidex/app/features/settings/pages/languages/language.component.ts
  8. 11
      src/Squidex/app/features/settings/pages/languages/languages-page.component.ts
  9. 25
      src/Squidex/app/framework/angular/panel-container.directive.ts
  10. 9
      src/Squidex/app/framework/angular/panel.component.ts
  11. 46
      src/Squidex/app/shared/services/app-languages.service.spec.ts
  12. 25
      src/Squidex/app/shared/services/app-languages.service.ts
  13. 18
      src/Squidex/app/shell/pages/internal/apps-menu.component.html
  14. 19
      src/Squidex/app/shell/pages/internal/apps-menu.component.ts
  15. 2
      tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/LanguagesConfigJsonTests.cs
  16. 28
      tests/Squidex.Domain.Apps.Read.Tests/Contents/GraphQLTests.cs

1
.drone.yml

@ -21,6 +21,7 @@ pipeline:
secrets: [ docker_username, docker_password ]
when:
event: push
branch: [master]
build_release:
image: docker

2
src/Squidex.Domain.Apps.Core.Model/Schemas/Schema.cs

@ -213,7 +213,7 @@ namespace Squidex.Domain.Apps.Core.Schemas
if (fieldsByName.ContainsKey(field.Name) || fieldsById.ContainsKey(field.Id))
{
throw new ArgumentException($"A field with name '{field.Name}' already exists.", nameof(field));
throw new ArgumentException($"A field with name '{field.Name}' and id {field.Id} already exists.", nameof(field));
}
return Clone(clone =>

4
src/Squidex.Domain.Apps.Read/Contents/GraphQL/GraphQLModel.cs

@ -71,6 +71,10 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL
typeof(JsonField),
field => ResolveDefault("Json")
},
{
typeof(TagsField),
field => ResolveDefault("String")
},
{
typeof(GeolocationField),
field => ResolveDefault("Geolocation")

3
src/Squidex/Controllers/Api/Ping/PingController.cs

@ -25,6 +25,7 @@ namespace Squidex.Controllers.Api.Ping
/// <summary>
/// Get ping status.
/// </summary>
/// <param name="app">The name of the app.</param>
/// <returns>
/// 204 => Service ping successful.
/// </returns>
@ -34,7 +35,7 @@ namespace Squidex.Controllers.Api.Ping
[HttpGet]
[Route("ping/{app}/")]
[ApiCosts(0)]
public IActionResult GetPing()
public IActionResult GetPing(string app)
{
return NoContent();
}

14
src/Squidex/Squidex.csproj

@ -15,15 +15,15 @@
<EmbeddedResource Include="Config\Identity\Cert\*.*;Docs\*.md" />
<EmbeddedResource Include="Controllers\Api\Users\Assets\Avatar.png" />
<EmbeddedResource Remove="Assets\**" />
<Compile Remove="Assets\**" />
<Content Remove="Assets\**" />
<None Remove="Assets\**" />
</ItemGroup>
<ItemGroup>
<None Update="dockerfile">
<None Update="dockerfile">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
</ItemGroup>
@ -60,7 +60,7 @@
<PackageReference Include="Microsoft.AspNetCore.HttpOverrides" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.ViewCompilation" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.ViewCompilation" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.0" />
@ -88,9 +88,9 @@
<ContentWithTargetPath Include="@(_DocumentationFile->'%(FullPath)')" RelativePath="%(_DocumentationFile.Identity)" TargetPath="%(_DocumentationFile.Filename)%(_DocumentationFile.Extension)" CopyToPublishDirectory="PreserveNewest" />
</ItemGroup>
</Target>
<PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
</Project>
</Project>

6
src/Squidex/app/features/api/pages/graphql/graphql-page.component.scss

@ -9,4 +9,8 @@
.graphiql-container > * {
box-sizing: content-box;
}
& .editorWrap {
overflow: hidden;
}
}

2
src/Squidex/app/features/settings/pages/languages/language.component.ts

@ -130,7 +130,7 @@ export class LanguageComponent implements OnInit, OnChanges, OnDestroy {
this.allLanguages.filter(l =>
this.language.iso2Code !== l.iso2Code &&
this.language.fallback.indexOf(l.iso2Code) < 0);
this.otherLanguage = this.otherLanguages.values[0];
this.otherLanguage = this.otherLanguages[0];
}
if (this.language) {

11
src/Squidex/app/features/settings/pages/languages/languages-page.component.ts

@ -106,16 +106,7 @@ export class LanguagesPageComponent implements OnInit {
this.appLanguages =
new AppLanguagesDto(
appLanguages.languages.map(l => {
const isMaster = masterId ? l.iso2Code === masterId : l.isMaster;
return new AppLanguageDto(
l.iso2Code,
l.englishName, isMaster,
l.isOptional,
l.fallback.filter(f => !!appLanguages.languages.find(l2 => l2.iso2Code === f))
);
}).sort((a, b) => {
appLanguages.languages.sort((a, b) => {
if (a.isMaster === b.isMaster) {
return a.iso2Code.localeCompare(b.iso2Code);
} else {

25
src/Squidex/app/framework/angular/panel-container.directive.ts

@ -25,7 +25,7 @@ export class PanelContainerDirective implements AfterViewInit, OnDestroy {
@HostListener('window:resize')
public onResize() {
this.invalidate();
this.invalidate(true);
}
public ngOnDestroy() {
@ -33,7 +33,7 @@ export class PanelContainerDirective implements AfterViewInit, OnDestroy {
}
public ngAfterViewInit() {
this.invalidate({ force: true, resize: true });
this.invalidate(true);
}
public push(panel: PanelComponent) {
@ -48,14 +48,8 @@ export class PanelContainerDirective implements AfterViewInit, OnDestroy {
this.invalidate();
}
public invalidate(params?: { force: boolean, resize: boolean }) {
this.isInit = this.isInit || (params && params.force) === true;
if (!this.isInit) {
return;
}
if (params && params.resize) {
public invalidate(resize = false) {
if (resize) {
this.containerWidth = this.element.nativeElement.getBoundingClientRect().width;
}
@ -67,21 +61,22 @@ export class PanelContainerDirective implements AfterViewInit, OnDestroy {
for (let panel of this.panels) {
const panelRoot = panel.panel.nativeElement;
let width = panel.desiredWidth;
let layoutWidth = '';
if (panel.desiredWidth === '*' && panel === last) {
panel.actualWidth = this.containerWidth - currentPosition;
panel.desiredWidth = width + 'px';
layoutWidth = (this.containerWidth - currentPosition) + 'px';
} else {
layoutWidth = panel.desiredWidth;
}
this.renderer.setElementStyle(panelRoot, 'top', '0px');
this.renderer.setElementStyle(panelRoot, 'left', currentPosition + 'px');
this.renderer.setElementStyle(panelRoot, 'width', layoutWidth);
this.renderer.setElementStyle(panelRoot, 'bottom', '0px');
this.renderer.setElementStyle(panelRoot, 'position', 'absolute');
this.renderer.setElementStyle(panelRoot, 'z-index', currentLayer.toString());
currentPosition += panel.actualWidth;
currentPosition += panel.renderWidth;
currentLayer -= 10;
}

9
src/Squidex/app/framework/angular/panel.component.ts

@ -14,7 +14,7 @@ import { PanelContainerDirective } from './panel-container.directive';
@Component({
selector: 'sqx-panel',
template: `
<div [style.width]="desiredWidth" [attr.expand]="expand" #panel>
<div #panel>
<div class="panel panel-{{theme}}" [@slideRight]>
<ng-content></ng-content>
</div>
@ -24,7 +24,7 @@ import { PanelContainerDirective } from './panel-container.directive';
]
})
export class PanelComponent implements AfterViewInit, OnDestroy, OnInit {
public actualWidth = 0;
public renderWidth = 0;
@Input()
public theme = 'light';
@ -32,9 +32,6 @@ export class PanelComponent implements AfterViewInit, OnDestroy, OnInit {
@Input()
public desiredWidth = '10rem';
@Input()
public expand = false;
@ViewChild('panel')
public panel: ElementRef;
@ -52,7 +49,7 @@ export class PanelComponent implements AfterViewInit, OnDestroy, OnInit {
}
public ngAfterViewInit() {
this.actualWidth = this.panel.nativeElement.getBoundingClientRect().width;
this.renderWidth = this.panel.nativeElement.getBoundingClientRect().width;
this.container.invalidate();
}

46
src/Squidex/app/shared/services/app-languages.service.spec.ts

@ -20,34 +20,56 @@ import {
} from './../';
describe('AppLanguageDto', () => {
const language1 = new AppLanguageDto('de', 'English', false, false, []);
const language2 = new AppLanguageDto('en', 'English', false, false, []);
const language2_new = new AppLanguageDto('en', 'English (United States)', false, false, []);
const version = new Version('1');
const newVersion = new Version('2');
it('should update languages when adding language', () => {
const languages_1 = new AppLanguagesDto([language1], version);
const languages_2 = languages_1.addLanguage(language2, newVersion);
const language1_1 = new AppLanguageDto('de', 'English', true, false, []);
const language2_1 = new AppLanguageDto('it', 'Italien', false, false, []);
expect(languages_2.languages).toEqual([language1, language2]);
const languages_1 = new AppLanguagesDto([language1_1], version);
const languages_2 = languages_1.addLanguage(language2_1, newVersion);
expect(languages_2.languages).toEqual([language1_1, language2_1]);
expect(languages_2.version).toEqual(newVersion);
});
it('should update languages when removing language', () => {
const languages_1 = new AppLanguagesDto([language1, language2], version);
const languages_2 = languages_1.removeLanguage(language1, newVersion);
const language1_1 = new AppLanguageDto('de', 'English', true, false, ['it']);
const language1_2 = new AppLanguageDto('de', 'English', true, false, []);
const language2_1 = new AppLanguageDto('it', 'Italien', false, false, []);
const languages_1 = new AppLanguagesDto([language1_1, language2_1], version);
const languages_2 = languages_1.removeLanguage(language2_1, newVersion);
expect(languages_2.languages).toEqual([language2]);
expect(languages_2.languages).toEqual([language1_2]);
expect(languages_2.version).toEqual(newVersion);
});
it('should update languages when updating language', () => {
const languages_1 = new AppLanguagesDto([language1, language2], version);
const languages_2 = languages_1.updateLanguage(language2_new, newVersion);
const language1_1 = new AppLanguageDto('de', 'English', true, false, ['it']);
const language2_1 = new AppLanguageDto('it', 'Italien', false, false, []);
const language2_2 = new AppLanguageDto('it', 'Italien', false, false, ['de']);
const languages_1 = new AppLanguagesDto([language1_1, language2_1], version);
const languages_2 = languages_1.updateLanguage(language2_2, newVersion);
expect(languages_2.languages).toEqual([language1_1, language2_2]);
expect(languages_2.version).toEqual(newVersion);
});
it('should update master language when updating language', () => {
const language1_1 = new AppLanguageDto('de', 'English', true, false, ['it']);
const language1_2 = new AppLanguageDto('de', 'English', false, false, ['it']);
const language2_1 = new AppLanguageDto('it', 'Italien', false, false, []);
const language2_2 = new AppLanguageDto('it', 'Italien', true, false, ['de']);
const languages_1 = new AppLanguagesDto([language1_1, language2_1], version);
const languages_2 = languages_1.updateLanguage(language2_2, newVersion);
expect(languages_2.languages).toEqual([language1, language2_new]);
expect(languages_2.languages).toEqual([language1_2, language2_2]);
expect(languages_2.version).toEqual(newVersion);
expect(languages_2.languages[0].isMaster).toBeFalsy();
});
});

25
src/Squidex/app/shared/services/app-languages.service.ts

@ -30,12 +30,29 @@ export class AppLanguagesDto {
return new AppLanguagesDto([...this.languages, language], version);
}
public updateLanguage(language: AppLanguageDto, version: Version) {
return new AppLanguagesDto(this.languages.map(l => l.iso2Code === language.iso2Code ? language : l), version);
public removeLanguage(language: AppLanguageDto, version: Version) {
return new AppLanguagesDto(
this.languages.filter(l => l.iso2Code !== language.iso2Code).map(l => {
return new AppLanguageDto(
l.iso2Code,
l.englishName,
l.isMaster,
l.isOptional,
l.fallback.filter(f => f !== language.iso2Code));
}), version);
}
public removeLanguage(language: AppLanguageDto, version: Version) {
return new AppLanguagesDto(this.languages.filter(l => l.iso2Code !== language.iso2Code), version);
public updateLanguage(language: AppLanguageDto, version: Version) {
return new AppLanguagesDto(
this.languages.map(l => {
if (l.iso2Code === language.iso2Code) {
return language;
} else if (l.isMaster && language.isMaster) {
return new AppLanguageDto(l.iso2Code, l.englishName, false, l.isOptional, l.fallback);
} else {
return l;
}
}), version);
}
}

18
src/Squidex/app/shell/pages/internal/apps-menu.component.html

@ -1,8 +1,8 @@
<ul class="nav navbar-nav" *ngIf="apps">
<li class="nav-item dropdown">
<span class="nav-link dropdown-toggle" id="app-name" (click)="modalMenu.toggle()">{{ctx.app ? ctx.app.name : 'Apps Overview'}}</span>
<span class="nav-link dropdown-toggle" id="app-name" (click)="appsMenu.toggle()">{{selectedApp ? selectedApp.name : 'Apps Overview'}}</span>
<div class="dropdown-menu" *sqxModalView="modalMenu" closeAlways="true" @fade>
<div class="dropdown-menu" *sqxModalView="appsMenu" closeAlways="true" @fade>
<a class="dropdown-item all-apps" routerLink="/app">
<span class="all-apps-text">All Apps</span>
<span class="all-apps-pill badge badge-pill badge-primary">{{apps.length}}</span>
@ -22,34 +22,34 @@
</div>
</li>
<li class="nav-item" *ngIf="ctx.app && ctx.app.planUpgrade && ctx.app.planName && ctx.app.permission === 'Owner'">
<li class="nav-item" *ngIf="selectedApp && selectedApp.planUpgrade && selectedApp.planName && selectedApp.permission === 'Owner'">
<div class="btn-group app-upgrade">
<button type="button" class="btn btn-primary">
You are on the <strong>{{ctx.app.planName}}</strong> plan.
You are on the <strong>{{selectedApp.planName}}</strong> plan.
</button>
<button type="button" class="btn btn-warning" [routerLink]="['/app', ctx.app.name, 'settings', 'plans']">
<button type="button" class="btn btn-warning" [routerLink]="['/app', selectedApp.name, 'settings', 'plans']">
Upgrade!
</button>
</div>
</li>
</ul>
<div class="modal" *sqxModalView="modalDialog;onRoot:true" @fade>
<div class="modal" *sqxModalView="addAppDialog;onRoot:true" @fade>
<div class="modal-backdrop"></div>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Create App</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close" (click)="modalDialog.hide()">
<button type="button" class="close" data-dismiss="modal" aria-label="Close" (click)="addAppDialog.hide()">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<sqx-app-form
(created)="modalDialog.hide()"
(cancelled)="modalDialog.hide()">
(created)="addAppDialog.hide()"
(cancelled)="addAppDialog.hide()">
</sqx-app-form>
</div>
</div>

19
src/Squidex/app/shell/pages/internal/apps-menu.component.ts

@ -29,19 +29,23 @@ import {
})
export class AppsMenuComponent implements OnDestroy, OnInit {
private appsSubscription: Subscription;
private appSubscription: Subscription;
public modalMenu = new ModalView(false, true);
public modalDialog = new ModalView();
public addAppDialog = new ModalView();
public appsMenu = new ModalView(false, true);
public apps: AppDto[] = [];
constructor(public readonly ctx: AppContext,
public selectedApp: AppDto;
constructor(
private readonly appsStore: AppsStoreService
) {
}
public ngOnDestroy() {
this.appsSubscription.unsubscribe();
this.appSubscription.unsubscribe();
}
public ngOnInit() {
@ -49,10 +53,15 @@ export class AppsMenuComponent implements OnDestroy, OnInit {
this.appsStore.apps.subscribe(apps => {
this.apps = apps;
});
this.appSubscription =
this.appsStore.selectedApp.subscribe(app => {
this.selectedApp = app;
});
}
public createApp() {
this.modalMenu.hide();
this.modalDialog.show();
this.appsMenu.hide();
this.addAppDialog.show();
}
}

2
tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/LanguagesConfigJsonTests.cs

@ -29,6 +29,8 @@ namespace Squidex.Domain.Apps.Core.Model.Apps
new LanguageConfig(Language.IT, false, Language.DE))
.MakeMaster(Language.IT);
sut.MakeMaster(Language.IT);
var serialized = JToken.FromObject(sut, serializer).ToObject<LanguagesConfig>(serializer);
serialized.ShouldBeEquivalentTo(sut);

28
tests/Squidex.Domain.Apps.Read.Tests/Contents/GraphQLTests.cs

@ -69,7 +69,9 @@ namespace Squidex.Domain.Apps.Read.Contents
.AddField(new ReferencesField(9, "my-invalid", Partitioning.Invariant,
new ReferencesFieldProperties { SchemaId = Guid.NewGuid() }))
.AddField(new GeolocationField(10, "my-geolocation", Partitioning.Invariant,
new GeolocationFieldProperties()));
new GeolocationFieldProperties()))
.AddField(new TagsField(11, "my-tags", Partitioning.Invariant,
new TagsFieldProperties()));
A.CallTo(() => app.Id).Returns(appId);
A.CallTo(() => app.Name).Returns(appName);
@ -266,6 +268,9 @@ namespace Squidex.Domain.Apps.Read.Contents
myGeolocation {
iv
}
myTags {
iv
}
}
}
}";
@ -326,6 +331,14 @@ namespace Squidex.Domain.Apps.Read.Contents
latitude = 10,
longitude = 20
}
},
myTags = new
{
iv = new[]
{
"tag1",
"tag2"
}
}
}
}
@ -371,6 +384,9 @@ namespace Squidex.Domain.Apps.Read.Contents
myGeolocation {{
iv
}}
myTags {{
iv
}}
}}
}}
}}";
@ -425,6 +441,14 @@ namespace Squidex.Domain.Apps.Read.Contents
latitude = 10,
longitude = 20
}
},
myTags = new
{
iv = new[]
{
"tag1",
"tag2"
}
}
}
}
@ -609,6 +633,8 @@ namespace Squidex.Domain.Apps.Read.Contents
new ContentFieldData().AddValue("iv", true))
.AddField("my-datetime",
new ContentFieldData().AddValue("iv", now.ToDateTimeUtc()))
.AddField("my-tags",
new ContentFieldData().AddValue("iv", JToken.FromObject(new[] { "tag1", "tag2" })))
.AddField("my-references",
new ContentFieldData().AddValue("iv", JToken.FromObject(new[] { refId })))
.AddField("my-geolocation",

Loading…
Cancel
Save