mirror of https://github.com/Squidex/squidex.git
69 changed files with 1072 additions and 251 deletions
@ -0,0 +1,46 @@ |
|||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netcoreapp1.0</TargetFramework> |
|||
<AssemblyName>Squidex.Core</AssemblyName> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<Compile Include="**\*.cs" /> |
|||
<EmbeddedResource Include="**\*.resx" /> |
|||
<EmbeddedResource Include="compiler\resources\**\*" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Squidex.Infrastructure\Squidex.Infrastructure.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.NET.Sdk"> |
|||
<Version>1.0.0-alpha-20161104-2</Version> |
|||
<PrivateAssets>All</PrivateAssets> |
|||
</PackageReference> |
|||
<PackageReference Include="NETStandard.Library"> |
|||
<Version>1.6.0</Version> |
|||
</PackageReference> |
|||
<PackageReference Include="NodaTime"> |
|||
<Version>2.0.0-alpha20160729</Version> |
|||
</PackageReference> |
|||
<PackageReference Include="protobuf-net"> |
|||
<Version>2.1.0</Version> |
|||
</PackageReference> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp1.0' "> |
|||
<PackageReference Include="Microsoft.NETCore.App"> |
|||
<Version>1.0.1</Version> |
|||
</PackageReference> |
|||
</ItemGroup> |
|||
|
|||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> |
|||
<DefineConstants>$(DefineConstants);RELEASE</DefineConstants> |
|||
</PropertyGroup> |
|||
|
|||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> |
|||
</Project> |
|||
@ -0,0 +1,63 @@ |
|||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netcoreapp1.0</TargetFramework> |
|||
<AssemblyName>Squidex.Infrastructure</AssemblyName> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<Compile Include="**\*.cs" /> |
|||
<EmbeddedResource Include="**\*.resx" Exclude="bin\**;obj\**;**\*.xproj;packages\**" /> |
|||
<EmbeddedResource Include="*.csv;compiler\resources\**\*" Exclude="bin\**;obj\**;**\*.xproj;packages\**" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.NET.Sdk"> |
|||
<Version>1.0.0-alpha-20161104-2</Version> |
|||
<PrivateAssets>All</PrivateAssets> |
|||
</PackageReference> |
|||
<PackageReference Include="Autofac"> |
|||
<Version>4.1.0</Version> |
|||
</PackageReference> |
|||
<PackageReference Include="EventStore.ClientAPI.NetCore"> |
|||
<Version>0.0.1-alpha</Version> |
|||
</PackageReference> |
|||
<PackageReference Include="Microsoft.Extensions.Logging"> |
|||
<Version>1.0.0</Version> |
|||
</PackageReference> |
|||
<PackageReference Include="NETStandard.Library"> |
|||
<Version>1.6.0</Version> |
|||
</PackageReference> |
|||
<PackageReference Include="Newtonsoft.Json"> |
|||
<Version>9.0.2-beta1</Version> |
|||
</PackageReference> |
|||
<PackageReference Include="NodaTime"> |
|||
<Version>2.0.0-alpha20160729</Version> |
|||
</PackageReference> |
|||
<PackageReference Include="protobuf-net"> |
|||
<Version>2.1.0</Version> |
|||
</PackageReference> |
|||
<PackageReference Include="System.Linq"> |
|||
<Version>4.1.0</Version> |
|||
</PackageReference> |
|||
<PackageReference Include="System.Reflection.TypeExtensions"> |
|||
<Version>4.1.0</Version> |
|||
</PackageReference> |
|||
<PackageReference Include="System.Security.Claims"> |
|||
<Version>4.0.1</Version> |
|||
</PackageReference> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp1.0' "> |
|||
<PackageReference Include="Microsoft.NETCore.App"> |
|||
<Version>1.0.1</Version> |
|||
</PackageReference> |
|||
</ItemGroup> |
|||
|
|||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> |
|||
<DefineConstants>$(DefineConstants);RELEASE</DefineConstants> |
|||
</PropertyGroup> |
|||
|
|||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> |
|||
</Project> |
|||
@ -1,10 +1,14 @@ |
|||
<div class="layout-content"> |
|||
<div class="layout-content-left layout-content-left--no-button"> |
|||
<div class="layout"> |
|||
<div class="layout-left"> |
|||
<sqx-left-menu></sqx-left-menu> |
|||
</div> |
|||
<div class="layout-content-main"> |
|||
<h1> |
|||
<i class="layout-title-icon icon-dashboard"></i> Dashboard |
|||
</h1> |
|||
<div class="layout-middle"> |
|||
<div class="layout-middle-header"> |
|||
<h1> |
|||
<i class="layout-title-icon icon-dashboard"></i> Dashboard |
|||
</h1> |
|||
</div> |
|||
<div class="layout-middle-content"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
@ -1,26 +1,38 @@ |
|||
<ul class="nav"> |
|||
<li class="nav-item"> |
|||
<a class="nav-link nav-link--dashboard" routerLink="../dashboard" routerLinkActive="active"> |
|||
<li class="nav-item nav-item--dashboard"> |
|||
<a class="nav-link" routerLink="../dashboard" routerLinkActive="active"> |
|||
<i class="icon-dashboard"></i> Dashboard |
|||
</a> |
|||
</li> |
|||
<li class="nav-item"> |
|||
<a class="nav-link nav-link--schemas" routerLink="../schemas" routerLinkActive="active"> |
|||
<li class="nav-items nav-item--schemas" *ngIf="permission !== 'Editor'"> |
|||
<a class="nav-link" routerLink="../schemas" routerLinkActive="active"> |
|||
<i class="icon-schemas"></i> Schemas |
|||
</a> |
|||
</li> |
|||
<li class="nav-item"> |
|||
<a class="nav-link nav-link--content"> |
|||
<li class="nav-item nav-item--content"> |
|||
<a class="nav-link"> |
|||
<i class="icon-content"></i> Content |
|||
</a> |
|||
</li> |
|||
<li class="nav-item"> |
|||
<a class="nav-link nav-link--media"> |
|||
<li class="nav-item nav-item--media"> |
|||
<a class="nav-link"> |
|||
<i class="icon-media"></i> Media</a> |
|||
</li> |
|||
<li class="nav-item"> |
|||
<a class="nav-link nav-link--settings"> |
|||
<li class="nav-item nav-item--settings nav-item-group" *ngIf="permission === 'Owner'"> |
|||
<a class="nav-link" (click)="toggleSettingsMenu()"> |
|||
<i class="icon-settings"></i> Settings |
|||
</a> |
|||
|
|||
<ul class="subnav" *ngIf="showSettingsMenu"> |
|||
<li class="subnav-item"> |
|||
<a class="nav-link" routerLink="../contributors" routerLinkActive="active">Contributors</a> |
|||
</li> |
|||
<li class="subnav-item"> |
|||
<a class="nav-link" routerLink="../credentials" routerLinkActive="active">Credentials</a> |
|||
</li> |
|||
<li class="subnav-item"> |
|||
<a class="nav-link" routerLink="../languages" routerLinkActive="active">Languages</a> |
|||
</li> |
|||
</ul> |
|||
</li> |
|||
</ul> |
|||
@ -1,12 +1,16 @@ |
|||
<div class="layout-content"> |
|||
<div class="layout-content-left"> |
|||
<button class="layout-new-button btn btn-success">Create Schema</button> |
|||
|
|||
<div class="layout"> |
|||
<div class="layout-left"> |
|||
<sqx-left-menu></sqx-left-menu> |
|||
</div> |
|||
<div class="layout-content-main"> |
|||
<h1> |
|||
<i class="layout-title-icon icon-schemas"></i> Schemas |
|||
</h1> |
|||
<div class="layout-middle"> |
|||
<div class="layout-middle-header"> |
|||
<button class="layout-new-button btn btn-success pull-right">Create Schema</button> |
|||
|
|||
<h1> |
|||
<i class="layout-title-icon icon-schemas"></i> Schemas |
|||
</h1> |
|||
</div> |
|||
<div class="layout-middle-content"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
@ -0,0 +1,67 @@ |
|||
<div class="layout"> |
|||
<div class="layout-left"> |
|||
<sqx-left-menu></sqx-left-menu> |
|||
</div> |
|||
<div class="layout-middle"> |
|||
<div class="layout-middle-header"> |
|||
<h1> |
|||
<i class="layout-title-icon icon-settings"></i> Contributors |
|||
</h1> |
|||
</div> |
|||
<div class="layout-middle-content"> |
|||
<div class="card"> |
|||
<div class="card-block"> |
|||
<table class="table table-borderless table-fixed"> |
|||
<colgroup> |
|||
<col style="width: 100%" /> |
|||
<col style="width: 150px" /> |
|||
<col style="width: 110px" /> |
|||
</colgroup> |
|||
|
|||
<tr *ngFor="let contributor of appContributors"> |
|||
<td class="col-xs-7"> |
|||
<img class="user-picture" [attr.src]="pictureUrl(contributor) | async" /> |
|||
|
|||
<span class="user-name"> |
|||
{{displayName(contributor) | async}} |
|||
</span> |
|||
<span class="user-email"> |
|||
{{email(contributor) | async}} |
|||
</span> |
|||
</td> |
|||
<td> |
|||
<select class="form-control" [(ngModel)]="contributor.permission" (ngModelChange)="saveContributor(contributor)" [disabled]="currrentUserId === contributor.contributorId"> |
|||
<option *ngFor="let permission of usersPermissions">{{permission}}</option> |
|||
</select> |
|||
</td> |
|||
<td> |
|||
<button class="btn btn-block btn-danger" [disabled]="currrentUserId === contributor.contributorId" (click)="revokeContributor(contributor)">Revoke</button> |
|||
</td> |
|||
</tr> |
|||
</table> |
|||
</div> |
|||
<div class="card-footer"> |
|||
<form class="form-inline" (submit)="addContributor()" > |
|||
<div class="form-group"> |
|||
<ng2-completer |
|||
[autoMatch]="true" |
|||
[dataService]="usersDataSource" |
|||
[minSearchLength]="3" |
|||
[placeholder]="'Search user by e-email'" |
|||
[pause]="300" |
|||
[clearSelected]="false" |
|||
[textSearching]="'Searching...'" |
|||
[inputName]="contributor" |
|||
[ngModel]="selectedUserName" |
|||
[ngModelOptions]="{standalone: true}" |
|||
(selected)="selectUser($event)"> |
|||
</ng2-completer> |
|||
</div> |
|||
|
|||
<button type="submit" class="btn btn-primary" [disabled]="!selectedUser">Add</button> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
@ -0,0 +1,28 @@ |
|||
@import '_vars'; |
|||
@import '_mixins'; |
|||
|
|||
.layout-title-icon { |
|||
color: $color-section-settings; |
|||
} |
|||
|
|||
.card { |
|||
max-width: 700px; |
|||
} |
|||
|
|||
.user { |
|||
&-picture { |
|||
@include circle(2.2rem); |
|||
float: left; |
|||
} |
|||
|
|||
&-name, |
|||
&-email { |
|||
@include truncate; |
|||
padding-left: 10px; |
|||
} |
|||
|
|||
&-email { |
|||
font-size: .8rem; |
|||
font-style: italic; |
|||
} |
|||
} |
|||
@ -0,0 +1,162 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Sebastian Stehle. All rights reserved |
|||
*/ |
|||
|
|||
import * as Ng2 from '@angular/core'; |
|||
|
|||
import { Observable, Subscription } from 'rxjs'; |
|||
|
|||
import { CompleterBaseData, CompleterItem } from 'ng2-completer'; |
|||
|
|||
import { |
|||
AppContributorDto, |
|||
AppContributorsService, |
|||
AppsStoreService, |
|||
AuthService, |
|||
TitleService, |
|||
UserDto, |
|||
UsersService, |
|||
UsersProviderService |
|||
} from 'shared'; |
|||
|
|||
class UsersDataSource extends CompleterBaseData { |
|||
private remoteSearch: Subscription; |
|||
|
|||
constructor( |
|||
private readonly usersService: UsersService, |
|||
private readonly component: ContributorsPageComponent, |
|||
) { |
|||
super(); |
|||
} |
|||
|
|||
public search(term: string): void { |
|||
this.cancel(); |
|||
|
|||
this.remoteSearch = |
|||
this.usersService.getUsers(term) |
|||
.map(users => { |
|||
const results: CompleterItem[] = []; |
|||
|
|||
for (let u of users) { |
|||
if (!this.component.appContributors || !this.component.appContributors.find(t => t.contributorId === u.id)) { |
|||
results.push({ title: u.displayName, image: u.pictureUrl, originalObject: u, description: u.email }); |
|||
} |
|||
} |
|||
|
|||
this.next(results); |
|||
|
|||
return results; |
|||
}) |
|||
.catch(err => { |
|||
this.error(err); |
|||
|
|||
return null; |
|||
}).subscribe(); |
|||
} |
|||
|
|||
public cancel() { |
|||
if (this.remoteSearch) { |
|||
this.remoteSearch.unsubscribe(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@Ng2.Component({ |
|||
selector: 'sqx-contributor-page', |
|||
styles, |
|||
template |
|||
}) |
|||
export class ContributorsPageComponent implements Ng2.OnInit { |
|||
private appSubscription: any | null = null; |
|||
private appName: string; |
|||
|
|||
public appContributors: AppContributorDto[]; |
|||
|
|||
public selectedUserName: string | null = null; |
|||
public selectedUser: UserDto | null = null; |
|||
|
|||
public currrentUserId: string; |
|||
|
|||
public usersDataSource: UsersDataSource; |
|||
public usersPermissions = [ |
|||
'Owner', |
|||
'Developer', |
|||
'Editor' |
|||
]; |
|||
|
|||
constructor( |
|||
private readonly titles: TitleService, |
|||
private readonly authService: AuthService, |
|||
private readonly appsStore: AppsStoreService, |
|||
private readonly appContributorsService: AppContributorsService, |
|||
private readonly usersProvider: UsersProviderService, |
|||
private readonly usersService: UsersService |
|||
) { |
|||
this.usersDataSource = new UsersDataSource(usersService, this); |
|||
} |
|||
|
|||
public ngOnInit() { |
|||
this.currrentUserId = this.authService.user.id; |
|||
|
|||
this.appSubscription = |
|||
this.appsStore.selectedApp.subscribe(app => { |
|||
if (app) { |
|||
this.appName = app.name; |
|||
|
|||
this.titles.setTitle('{appName} | Settings | Contributors', { appName: app.name }); |
|||
|
|||
this.appContributorsService.getContributors(app.name).subscribe(contributors => { |
|||
this.appContributors = contributors; |
|||
}); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public ngOnDestroy() { |
|||
this.appSubscription.unsubscribe(); |
|||
} |
|||
|
|||
public addContributor() { |
|||
if (!this.selectedUser) { |
|||
return; |
|||
} |
|||
|
|||
const contributor = new AppContributorDto(this.selectedUser.id, 'Editor'); |
|||
|
|||
this.appContributorsService.postContributor(this.appName, contributor).subscribe(); |
|||
this.appContributors.push(contributor); |
|||
|
|||
this.selectedUser = null; |
|||
this.selectedUserName = null; |
|||
} |
|||
|
|||
public revokeContributor(contributor: AppContributorDto) { |
|||
this.appContributorsService.deleteContributor(this.appName, contributor.contributorId).subscribe(); |
|||
|
|||
this.appContributors.splice(this.appContributors.indexOf(contributor), 1); |
|||
} |
|||
|
|||
public saveContributor(contributor: AppContributorDto) { |
|||
this.appContributorsService.postContributor(this.appName, contributor).subscribe(); |
|||
} |
|||
|
|||
public selectUser(selection: CompleterItem | null) { |
|||
this.selectedUser = selection ? selection.originalObject : null; |
|||
} |
|||
|
|||
public email(contributor: AppContributorDto): Observable<string> { |
|||
return this.usersProvider.getUser(contributor.contributorId).map(u => u.email); |
|||
} |
|||
|
|||
public displayName(contributor: AppContributorDto): Observable<string> { |
|||
return this.usersProvider.getUser(contributor.contributorId).map(u => u.displayName); |
|||
} |
|||
|
|||
public pictureUrl(contributor: AppContributorDto): Observable<string> { |
|||
return this.usersProvider.getUser(contributor.contributorId).map(u => u.pictureUrl); |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,14 @@ |
|||
<div class="layout"> |
|||
<div class="layout-left"> |
|||
<sqx-left-menu></sqx-left-menu> |
|||
</div> |
|||
<div class="layout-middle"> |
|||
<div class="layout-middle-header"> |
|||
<h1> |
|||
<i class="layout-title-icon icon-settings"></i> Credentials |
|||
</h1> |
|||
</div> |
|||
<div class="layout-middle-content"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
@ -0,0 +1,6 @@ |
|||
@import '_vars'; |
|||
@import '_mixins'; |
|||
|
|||
.layout-title-icon { |
|||
color: $color-section-settings; |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Sebastian Stehle. All rights reserved |
|||
*/ |
|||
|
|||
import * as Ng2 from '@angular/core'; |
|||
|
|||
import { AppsStoreService, TitleService } from 'shared'; |
|||
|
|||
@Ng2.Component({ |
|||
selector: 'sqx-credentials-page', |
|||
styles, |
|||
template |
|||
}) |
|||
export class CredentialsPageComponent implements Ng2.OnInit { |
|||
private appSubscription: any | null = null; |
|||
|
|||
constructor( |
|||
private readonly titles: TitleService, |
|||
private readonly appsStore: AppsStoreService |
|||
) { |
|||
} |
|||
|
|||
public ngOnInit() { |
|||
this.appSubscription = |
|||
this.appsStore.selectedApp.subscribe(app => { |
|||
if (app) { |
|||
this.titles.setTitle('{appName} | Settings | Credentials', { appName: app.name }); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public ngOnDestroy() { |
|||
this.appSubscription.unsubscribe(); |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,14 @@ |
|||
<div class="layout"> |
|||
<div class="layout-left"> |
|||
<sqx-left-menu></sqx-left-menu> |
|||
</div> |
|||
<div class="layout-middle"> |
|||
<div class="layout-middle-header"> |
|||
<h1> |
|||
<i class="layout-title-icon icon-settings"></i> Languages |
|||
</h1> |
|||
</div> |
|||
<div class="layout-middle-content"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
@ -0,0 +1,6 @@ |
|||
@import '_vars'; |
|||
@import '_mixins'; |
|||
|
|||
.layout-title-icon { |
|||
color: $color-section-settings; |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Sebastian Stehle. All rights reserved |
|||
*/ |
|||
|
|||
import * as Ng2 from '@angular/core'; |
|||
|
|||
import { AppsStoreService, TitleService } from 'shared'; |
|||
|
|||
@Ng2.Component({ |
|||
selector: 'sqx-languages-page', |
|||
styles, |
|||
template |
|||
}) |
|||
export class LanguagesPageComponent implements Ng2.OnInit { |
|||
private appSubscription: any | null = null; |
|||
|
|||
constructor( |
|||
private readonly titles: TitleService, |
|||
private readonly appsStore: AppsStoreService |
|||
) { |
|||
} |
|||
|
|||
public ngOnInit() { |
|||
this.appSubscription = |
|||
this.appsStore.selectedApp.subscribe(app => { |
|||
if (app) { |
|||
this.titles.setTitle('{appName} | Settings | Languages', { appName: app.name }); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public ngOnDestroy() { |
|||
this.appSubscription.unsubscribe(); |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,39 @@ |
|||
@import '_mixins'; |
|||
@import '_vars'; |
|||
|
|||
.completer-dropdown { |
|||
& { |
|||
margin-top: 2px !important; |
|||
width: 400px !important; |
|||
} |
|||
|
|||
.completer { |
|||
&-image { |
|||
@include circle(2.2rem); |
|||
} |
|||
|
|||
&-row { |
|||
& { |
|||
@include clearfix; |
|||
width: auto !important; |
|||
margin: 0; |
|||
margin-bottom: 0 !important; |
|||
display: block !important; |
|||
} |
|||
} |
|||
|
|||
&-selected-row { |
|||
background: $color-theme-blue-dark !important; |
|||
} |
|||
|
|||
&-description { |
|||
font-style: italic; |
|||
} |
|||
|
|||
&-title, |
|||
&-description { |
|||
@include truncate; |
|||
padding-left: .3rem; |
|||
} |
|||
} |
|||
} |
|||
@ -1,2 +1,3 @@ |
|||
@import '_bootstrap.scss'; |
|||
@import '_layout.scss'; |
|||
@import '_layout.scss'; |
|||
@import '_completer.scss'; |
|||
Loading…
Reference in new issue