Browse Source

Temporary progress

pull/65/head
Sebastian Stehle 10 years ago
parent
commit
5c7ae0f653
  1. 50
      src/Squidex.Infrastructure/Assets/FolderAssetStore.cs
  2. 7
      src/Squidex.Infrastructure/Assets/IAssetStore.cs
  3. 2
      src/Squidex.Infrastructure/Assets/IAssetThumbnailGenerator.cs
  4. 21
      src/Squidex.Infrastructure/Assets/ImageSharp/ImageSharpAssetThumbnailGenerator.cs
  5. 58
      src/Squidex/Config/Domain/AssetStoreModule.cs
  6. 2
      src/Squidex/Config/Domain/EventPublishersModule.cs
  7. 5
      src/Squidex/Config/Domain/WriteModule.cs
  8. 36
      src/Squidex/Controllers/Api/Assets/AssetsController.cs
  9. 11
      src/Squidex/app-config/webpack.config.js
  10. 3
      src/Squidex/app/app.routes.ts
  11. 8
      src/Squidex/app/features/assets/declarations.ts
  12. 9
      src/Squidex/app/features/assets/index.ts
  13. 33
      src/Squidex/app/features/assets/module.ts
  14. 19
      src/Squidex/app/features/assets/pages/assets-page.component.html
  15. 2
      src/Squidex/app/features/assets/pages/assets-page.component.scss
  16. 31
      src/Squidex/app/features/assets/pages/assets-page.component.ts
  17. 4
      src/Squidex/app/features/dashboard/pages/dashboard-page.component.ts
  18. 1
      src/Squidex/app/shared/declarations.ts
  19. 2
      src/Squidex/app/shared/module.ts
  20. 52
      src/Squidex/app/shared/services/assets.service.ts
  21. 5
      src/Squidex/app/shell/pages/app/left-menu.component.html
  22. 6
      src/Squidex/appsettings.json

50
src/Squidex.Infrastructure/Assets/Physical/PhysicalAssetStorage.cs → src/Squidex.Infrastructure/Assets/FolderAssetStore.cs

@ -1,24 +1,28 @@
// ==========================================================================
// PhysicalAssetStorage.cs
// FolderAssetStorage.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.IO;
using System.Threading.Tasks;
using Squidex.Infrastructure.Log;
namespace Squidex.Infrastructure.Assets.Physical
namespace Squidex.Infrastructure.Assets
{
public sealed class PhysicalAssetStorage : IAssetStorage, IExternalSystem
public sealed class FolderAssetStore : IAssetStore, IExternalSystem
{
private readonly ISemanticLog log;
private readonly DirectoryInfo directory;
public PhysicalAssetStorage(string path)
public FolderAssetStore(string path, ISemanticLog log)
{
Guard.NotNullOrEmpty(path, nameof(path));
Guard.NotNull(log, nameof(log));
this.log = log;
directory = new DirectoryInfo(path);
}
@ -31,6 +35,10 @@ namespace Squidex.Infrastructure.Assets.Physical
{
directory.Create();
}
log.LogInformation(w => w
.WriteProperty("action", "FolderAssetStoreConfigured")
.WriteProperty("path", directory.FullName));
}
catch
{
@ -41,23 +49,30 @@ namespace Squidex.Infrastructure.Assets.Physical
}
}
public Task<Stream> GetAssetAsync(Guid id, string tags = null)
public Task<Stream> GetAssetAsync(string name)
{
var file = GetFile(id, tags);
var file = GetFile(name);
Stream stream = null;
if (file.Exists)
try
{
stream = file.OpenRead();
if (file.Exists)
{
stream = file.OpenRead();
}
}
catch (FileNotFoundException)
{
stream = null;
}
return Task.FromResult(stream);
}
public async Task UploadAssetAsync(Guid id, Stream stream, string tags = null)
public async Task UploadAssetAsync(string name, Stream stream)
{
var file = GetFile(id, tags);
var file = GetFile(name);
using (var fileStream = file.OpenWrite())
{
@ -65,18 +80,11 @@ namespace Squidex.Infrastructure.Assets.Physical
}
}
private FileInfo GetFile(Guid id, string tags)
private FileInfo GetFile(string name)
{
var fileName = id.ToString();
if (!string.IsNullOrWhiteSpace(tags))
{
fileName += tags;
}
Guard.ValidFileName(fileName, tags);
Guard.ValidFileName(name, nameof(name));
var file = new FileInfo(Path.Combine(directory.FullName, fileName));
var file = new FileInfo(Path.Combine(directory.FullName, name));
return file;
}

7
src/Squidex.Infrastructure/Assets/IAssetStorage.cs → src/Squidex.Infrastructure/Assets/IAssetStore.cs

@ -6,16 +6,15 @@
// All rights reserved.
// ==========================================================================
using System;
using System.IO;
using System.Threading.Tasks;
namespace Squidex.Infrastructure.Assets
{
public interface IAssetStorage
public interface IAssetStore
{
Task<Stream> GetAssetAsync(Guid id, string tags = null);
Task<Stream> GetAssetAsync(string name);
Task UploadAssetAsync(Guid id, Stream stream, string tags = null);
Task UploadAssetAsync(string name, Stream stream);
}
}

2
src/Squidex.Infrastructure/Assets/IAssetThumbnailGenerator.cs

@ -13,6 +13,8 @@ namespace Squidex.Infrastructure.Assets
{
public interface IAssetThumbnailGenerator
{
Task<bool> IsValidImageAsync(Stream input);
Task<Stream> GetThumbnailOrNullAsync(Stream input, int dimension);
}
}

21
src/Squidex.Infrastructure/Assets/ImageSharp/ImageSharpAssetThumbnailGenerator.cs

@ -24,7 +24,7 @@ namespace Squidex.Infrastructure.Assets.ImageSharp
public Task<Stream> GetThumbnailOrNullAsync(Stream input, int dimension)
{
return Task.Run<Stream>(() =>
return Task.Run(() =>
{
var result = new MemoryStream();
@ -39,7 +39,24 @@ namespace Squidex.Infrastructure.Assets.ImageSharp
image.Save(result);
return result;
return (Stream)result;
});
}
public Task<bool> IsValidImageAsync(Stream input)
{
return Task.Run(() =>
{
try
{
var image = new Image(input);
return image.Width > 0 && image.Height > 0;
}
catch
{
return false;
}
});
}
}

58
src/Squidex/Config/Domain/AssetStoreModule.cs

@ -0,0 +1,58 @@
// ==========================================================================
// AssetStoreModule.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Autofac;
using Microsoft.Extensions.Configuration;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Assets;
using Squidex.Infrastructure.Log;
// ReSharper disable InvertIf
namespace Squidex.Config.Domain
{
public sealed class AssetStoreModule : Module
{
private IConfiguration Configuration { get; }
public AssetStoreModule(IConfiguration configuration)
{
Configuration = configuration;
}
protected override void Load(ContainerBuilder builder)
{
var assetStoreType = Configuration.GetValue<string>("assetStore:type");
if (string.IsNullOrWhiteSpace(assetStoreType))
{
throw new ConfigurationException("Configure the AssetStore type with 'assetStore:type'.");
}
if (string.Equals(assetStoreType, "Folder", StringComparison.OrdinalIgnoreCase))
{
var path = Configuration.GetValue<string>("assetStore:folder:path");
if (string.IsNullOrWhiteSpace(path))
{
throw new ConfigurationException("Configure AssetStore Folder path with 'assetStore:folder:path'.");
}
builder.Register(c => new FolderAssetStore(path, c.Resolve<ISemanticLog>()))
.As<IAssetStore>()
.As<IExternalSystem>()
.SingleInstance();
}
else
{
throw new ConfigurationException($"Unsupported value '{assetStoreType}' for 'assetStore:type', supported: Folder.");
}
}
}
}

2
src/Squidex/Config/Domain/EventPublishersModule.cs

@ -1,5 +1,5 @@
// ==========================================================================
// RabbitMqModule.cs
// EventPublishersModule.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group

5
src/Squidex/Config/Domain/WriteModule.cs

@ -12,6 +12,7 @@ using Squidex.Core.Schemas;
using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Pipeline.CommandHandlers;
using Squidex.Write.Apps;
using Squidex.Write.Assets;
using Squidex.Write.Contents;
using Squidex.Write.Schemas;
@ -78,6 +79,10 @@ namespace Squidex.Config.Domain
.AsSelf()
.SingleInstance();
builder.Register<DomainObjectFactoryFunction<AssetDomainObject>>(c => (id => new AssetDomainObject(id, -1)))
.AsSelf()
.SingleInstance();
builder.Register<DomainObjectFactoryFunction<ContentDomainObject>>(c => (id => new ContentDomainObject(id, -1)))
.AsSelf()
.SingleInstance();

36
src/Squidex/Controllers/Api/Assets/AssetsController.cs

@ -32,20 +32,20 @@ namespace Squidex.Controllers.Api.Assets
[SwaggerTag("Assets")]
public class AssetsController : ControllerBase
{
private readonly IAssetStorage assetStorage;
private readonly IAssetStore assetStorage;
private readonly IAssetThumbnailGenerator assetThumbnailGenerator;
private readonly AssetConfig assetsConfig;
private readonly IAssetThumbnailGenerator thumbnailGenerator;
public AssetsController(
ICommandBus commandBus,
IAssetStorage assetStorage,
IAssetThumbnailGenerator thumbnailGenerator,
IAssetStore assetStorage,
IAssetThumbnailGenerator assetThumbnailGenerator,
IOptions<AssetConfig> assetsConfig)
: base(commandBus)
{
this.assetStorage = assetStorage;
this.assetThumbnailGenerator = assetThumbnailGenerator;
this.assetsConfig = assetsConfig.Value;
this.thumbnailGenerator = thumbnailGenerator;
}
/// <summary>
@ -72,32 +72,24 @@ namespace Squidex.Controllers.Api.Assets
throw new ValidationException("Cannot create asset.", error);
}
var fileContent = new MemoryStream();
await file.OpenReadStream().CopyToAsync(fileContent);
fileContent.Position = 0;
var command = new CreateAsset
{
AssetId = Guid.NewGuid(),
FileSize = file.Length,
FileName = file.Name,
MimeType = file.ContentType
MimeType = file.ContentType,
IsImage = await assetThumbnailGenerator.IsValidImageAsync(fileContent)
};
var fileContent = new MemoryStream();
await file.CopyToAsync(fileContent);
fileContent.Position = 0;
var fileThumbnail = await thumbnailGenerator.GetThumbnailOrNullAsync(fileContent, 200);
if (fileThumbnail != null)
{
command.IsImage = true;
await assetStorage.UploadAssetAsync(command.AssetId, fileThumbnail, "thumbnail");
}
fileContent.Position = 0;
await assetStorage.UploadAssetAsync(command.AssetId, fileContent);
await assetStorage.UploadAssetAsync($"{command.AssetId}_0", fileContent);
var context = await CommandBus.PublishAsync(command);

11
src/Squidex/app-config/webpack.config.js

@ -48,9 +48,16 @@ module.exports = {
loader: 'angular2-router-loader'
}, {
loader: 'angular2-template-loader'
},{
}, {
loader: 'tslint-loader'
}]
}],
exclude: /node_modules/
},{
test: /\.ts$/,
use: [{
loader: 'awesome-typescript-loader'
}],
include: /node_modules/
}, {
test: /\.html$/,
use: [{

3
src/Squidex/app/app.routes.ts

@ -53,6 +53,9 @@ export const routes: Routes = [
}, {
path: 'schemas',
loadChildren: './features/schemas/module#SqxFeatureSchemasModule'
}, {
path: 'assets',
loadChildren: './features/assets/module#SqxFeatureAssetsModule'
}, {
path: 'settings',
loadChildren: './features/settings/module#SqxFeatureSettingsModule'

8
src/Squidex/app/features/assets/declarations.ts

@ -0,0 +1,8 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Sebastian Stehle. All rights reserved
*/
export * from './pages/assets-page.component';

9
src/Squidex/app/features/assets/index.ts

@ -0,0 +1,9 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Sebastian Stehle. All rights reserved
*/
export * from './declarations';
export * from './module';

33
src/Squidex/app/features/assets/module.ts

@ -0,0 +1,33 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Sebastian Stehle. All rights reserved
*/
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { SqxFrameworkModule } from 'shared';
import {
AssetsPageComponent
} from './declarations';
const routes: Routes = [
{
path: '',
component: AssetsPageComponent
}
];
@NgModule({
imports: [
SqxFrameworkModule,
RouterModule.forChild(routes)
],
declarations: [
AssetsPageComponent
]
})
export class SqxFeatureAssetsModule { }

19
src/Squidex/app/features/assets/pages/assets-page.component.html

@ -0,0 +1,19 @@
<sqx-title message="{app} | Assets" parameter1="app" value1="{{appName() | async}}"></sqx-title>
<sqx-panel panelWidth="60rem">
<div class="panel-header">
<div class="panel-title-row">
<h3 class="panel-title">Assets</h3>
</div>
<a class="panel-close" routerLink="../">
<i class="icon-close"></i>
</a>
</div>
<div class="panel-main">
<div class="panel-content panel-content-scroll">
</div>
</div>
</sqx-panel>

2
src/Squidex/app/features/assets/pages/assets-page.component.scss

@ -0,0 +1,2 @@
@import '_vars';
@import '_mixins';

31
src/Squidex/app/features/assets/pages/assets-page.component.ts

@ -0,0 +1,31 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Sebastian Stehle. All rights reserved
*/
import { Component } from '@angular/core';
import {
AppComponentBase,
AppsStoreService,
fadeAnimation,
NotificationService,
UsersProviderService
} from 'shared';
@Component({
selector: 'sqx-assets-page',
styleUrls: ['./assets-page.component.scss'],
templateUrl: './assets-page.component.html',
animations: [
fadeAnimation
]
})
export class AssetsPageComponent extends AppComponentBase {
constructor(apps: AppsStoreService, notifications: NotificationService, users: UsersProviderService) {
super(notifications, users, apps);
}
}

4
src/Squidex/app/features/dashboard/pages/dashboard-page.component.ts

@ -5,7 +5,7 @@
* Copyright (c) Sebastian Stehle. All rights reserved
*/
import { Component } from '@angular/core';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription} from 'rxjs';
import {
@ -27,7 +27,7 @@ declare var _urq: any;
fadeAnimation
]
})
export class DashboardPageComponent extends AppComponentBase {
export class DashboardPageComponent extends AppComponentBase implements OnInit, OnDestroy {
private authenticationSubscription: Subscription;
public profileDisplayName = '';

1
src/Squidex/app/shared/declarations.ts

@ -26,6 +26,7 @@ export * from './services/app-clients.service';
export * from './services/app-languages.service';
export * from './services/apps-store.service';
export * from './services/apps.service';
export * from './services/assets.service';
export * from './services/auth.service';
export * from './services/contents.service';
export * from './services/event-consumers.service';

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

@ -17,6 +17,7 @@ import {
AppsStoreService,
AppsService,
AppMustExistGuard,
AssetsService,
AuthService,
ContentsService,
DashboardLinkDirective,
@ -69,6 +70,7 @@ export class SqxSharedModule {
AppsStoreService,
AppsService,
AppMustExistGuard,
AssetsService,
AuthService,
ContentsService,
EventConsumersService,

52
src/Squidex/app/shared/services/assets.service.ts

@ -0,0 +1,52 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Sebastian Stehle. All rights reserved
*/
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs';
import { ApiUrlConfig, EntityCreatedDto } from 'framework';
import { AuthService } from './auth.service';
@Injectable()
export class AssetsService {
constructor(
private readonly http: Http,
private readonly apiUrl: ApiUrlConfig,
private readonly authService: AuthService
) {
}
public uploadFile(appName: string, file: File): Observable<number | EntityCreatedDto> {
return new Observable<number | EntityCreatedDto>(subscriber => {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas`);
const content = new FormData();
const headers = new Headers({
'Authorization': `${this.authService.user.user.token_type} ${this.authService.user.user.access_token}`,
'Content-Type': 'multipart/form-data'
});
content.append('file', file);
this.http
.post(url, content, headers)
.map(response => response.json())
.map(response => {
return new EntityCreatedDto(response.id);
})
.catchError('Failed to upload asset. Please reload.')
.subscribe(value => {
subscriber.next(value);
}, err => {
subscriber.error(err);
}, () => {
subscriber.complete();
});
});
}
}

5
src/Squidex/app/shell/pages/app/left-menu.component.html

@ -10,6 +10,11 @@
<i class="nav-icon icon-content"></i> <div class="nav-text">Content</div>
</a>
</li>
<li class="nav-item">
<a class="nav-link" routerLink="assets" routerLinkActive="active">
<i class="nav-icon icon-media"></i> <div class="nav-text">Assets</div>
</a>
</li>
<li class="nav-item" *ngIf="permission === 'Owner'">
<a class="nav-link" routerLink="settings" routerLinkActive="active">
<i class="nav-icon icon-settings"></i> <div class="nav-text">Settings</div>

6
src/Squidex/appsettings.json

@ -11,6 +11,12 @@
"configuration": "localhost:6379,resolveDns=1"
}
},
"assetStore": {
"type": "Folder",
"folder": {
"path": "Assets"
}
},
"eventStore": {
"type": "MongoDb",
"mongoDb": {

Loading…
Cancel
Save