Browse Source

Closes #62, Provide fallback image for user profile

pull/65/head
Sebastian Stehle 9 years ago
parent
commit
681b1411fa
  1. 22
      src/Squidex.Read/Users/UserExtensions.cs
  2. BIN
      src/Squidex/Controllers/Api/Users/Assets/Avatar.png
  3. 61
      src/Squidex/Controllers/Api/Users/UsersController.cs
  4. 1
      src/Squidex/Squidex.csproj
  5. 2
      src/Squidex/app/features/administration/pages/users/users-page.component.html
  6. 33
      src/Squidex/app/shared/components/pipes.ts
  7. 3
      src/Squidex/app/shared/module.ts

22
src/Squidex.Read/Users/UserExtensions.cs

@ -6,10 +6,13 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System;
using System.Linq; using System.Linq;
using Squidex.Core.Identity; using Squidex.Core.Identity;
using Squidex.Infrastructure; using Squidex.Infrastructure;
// ReSharper disable InvertIf
namespace Squidex.Read.Users namespace Squidex.Read.Users
{ {
public static class UserExtensions public static class UserExtensions
@ -38,5 +41,24 @@ namespace Squidex.Read.Users
{ {
return user.Claims.FirstOrDefault(x => x.Type == SquidexClaimTypes.SquidexDisplayName)?.Value; return user.Claims.FirstOrDefault(x => x.Type == SquidexClaimTypes.SquidexDisplayName)?.Value;
} }
public static string PictureNormalizedUrl(this IUser user)
{
var url = user.Claims.FirstOrDefault(x => x.Type == SquidexClaimTypes.SquidexPictureUrl)?.Value;
if (!string.IsNullOrWhiteSpace(url) && Uri.IsWellFormedUriString(url, UriKind.Absolute) && url.Contains("gravatar"))
{
if (url.Contains("?"))
{
url += "&d=404";
}
else
{
url += "?d=404";
}
}
return url;
}
} }
} }

BIN
src/Squidex/Controllers/Api/Users/Assets/Avatar.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

61
src/Squidex/Controllers/Api/Users/UsersController.cs

@ -6,7 +6,10 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
@ -17,18 +20,32 @@ using Squidex.Infrastructure.Reflection;
using Squidex.Pipeline; using Squidex.Pipeline;
using Squidex.Read.Users; using Squidex.Read.Users;
// ReSharper disable InvertIf
namespace Squidex.Controllers.Api.Users namespace Squidex.Controllers.Api.Users
{ {
/// <summary> /// <summary>
/// Readonly API to retrieve information about squidex users. /// Readonly API to retrieve information about squidex users.
/// </summary> /// </summary>
[Authorize]
[ApiExceptionFilter] [ApiExceptionFilter]
[SwaggerTag("Users")] [SwaggerTag("Users")]
public class UsersController : Controller public class UsersController : Controller
{ {
private static readonly byte[] AvatarBytes;
private readonly UserManager<IUser> userManager; private readonly UserManager<IUser> userManager;
static UsersController()
{
var assembly = typeof(UsersController).GetTypeInfo().Assembly;
using (var avatarStream = assembly.GetManifestResourceStream("Squidex.Controllers.Api.Users.Assets.Avatar.png"))
{
AvatarBytes = new byte[avatarStream.Length];
avatarStream.Read(AvatarBytes, 0, AvatarBytes.Length);
}
}
public UsersController(UserManager<IUser> userManager) public UsersController(UserManager<IUser> userManager)
{ {
this.userManager = userManager; this.userManager = userManager;
@ -44,6 +61,7 @@ namespace Squidex.Controllers.Api.Users
/// <returns> /// <returns>
/// 200 => Users returned. /// 200 => Users returned.
/// </returns> /// </returns>
[Authorize]
[HttpGet] [HttpGet]
[Route("users")] [Route("users")]
[ProducesResponseType(typeof(UserDto[]), 200)] [ProducesResponseType(typeof(UserDto[]), 200)]
@ -64,6 +82,7 @@ namespace Squidex.Controllers.Api.Users
/// 200 => User found. /// 200 => User found.
/// 404 => User not found. /// 404 => User not found.
/// </returns> /// </returns>
[Authorize]
[HttpGet] [HttpGet]
[Route("users/{id}/")] [Route("users/{id}/")]
[ProducesResponseType(typeof(UserDto), 200)] [ProducesResponseType(typeof(UserDto), 200)]
@ -79,6 +98,44 @@ namespace Squidex.Controllers.Api.Users
var response = SimpleMapper.Map(entity, new UserDto { DisplayName = entity.DisplayName(), PictureUrl = entity.PictureUrl() }); var response = SimpleMapper.Map(entity, new UserDto { DisplayName = entity.DisplayName(), PictureUrl = entity.PictureUrl() });
return Ok(response); return Ok(response);
} }
/// <summary>
/// Get user picture by id.
/// </summary>
/// <param name="id">The id of the user (GUID).</param>
/// <returns>
/// 200 => User found and image or fallback returned.
/// 404 => User not found.
/// </returns>
[HttpGet]
[Route("users/{id}/picture")]
[ProducesResponseType(200)]
public async Task<IActionResult> GetUserPicture(string id)
{
var entity = await userManager.FindByIdAsync(id);
if (entity == null)
{
return NotFound();
}
using (var client = new HttpClient())
{
var url = entity.PictureNormalizedUrl();
if (!string.IsNullOrWhiteSpace(url))
{
var response = await client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
return new FileStreamResult(await response.Content.ReadAsStreamAsync(), response.Content.Headers.ContentType.ToString());
}
}
}
return new FileStreamResult(new MemoryStream(AvatarBytes), "image/png");
}
} }
} }

1
src/Squidex/Squidex.csproj

@ -15,6 +15,7 @@
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Config\Identity\Cert\*.*;Docs\*.md" /> <EmbeddedResource Include="Config\Identity\Cert\*.*;Docs\*.md" />
<EmbeddedResource Include="Controllers\Api\Users\Assets\Avatar.png" />
<EmbeddedResource Remove="Assets\**" /> <EmbeddedResource Remove="Assets\**" />
<Compile Remove="Assets\**" /> <Compile Remove="Assets\**" />
<Content Remove="Assets\**" /> <Content Remove="Assets\**" />

2
src/Squidex/app/features/administration/pages/users/users-page.component.html

@ -60,7 +60,7 @@
<ng-template ngFor let-user [ngForOf]="usersItems"> <ng-template ngFor let-user [ngForOf]="usersItems">
<tr [routerLink]="user.id" routerLinkActive="active"> <tr [routerLink]="user.id" routerLinkActive="active">
<td> <td>
<img class="user-picture" [attr.title]="user.name" [attr.src]="user.pictureUrl" /> <img class="user-picture" [attr.title]="user.name" [attr.src]="user | userDtoPicture" />
</td> </td>
<td> <td>
<span class="user-name table-cell">{{user.displayName}}</span> <span class="user-name table-cell">{{user.displayName}}</span>

33
src/Squidex/app/shared/components/pipes.ts

@ -8,7 +8,9 @@
import { ChangeDetectorRef, OnDestroy, Pipe, PipeTransform } from '@angular/core'; import { ChangeDetectorRef, OnDestroy, Pipe, PipeTransform } from '@angular/core';
import { Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
import { UsersProviderService } from './../declarations-base'; import { ApiUrlConfig } from 'framework';
import { UserDto, UsersProviderService } from './../declarations-base';
class UserAsyncPipe implements OnDestroy { class UserAsyncPipe implements OnDestroy {
private lastUserId: string; private lastUserId: string;
@ -122,17 +124,34 @@ export class UserEmailRefPipe extends UserAsyncPipe implements PipeTransform {
} }
} }
@Pipe({
name: 'userDtoPicture',
pure: false
})
export class UserDtoPicture implements PipeTransform {
constructor(
private readonly apiUrl: ApiUrlConfig
) {
}
public transform(user: UserDto): string | null {
return this.apiUrl.buildUrl(`api/users/${user.id}/picture`);
}
}
@Pipe({ @Pipe({
name: 'userPicture', name: 'userPicture',
pure: false pure: false
}) })
export class UserPicturePipe extends UserAsyncPipe implements PipeTransform { export class UserPicturePipe extends UserAsyncPipe implements PipeTransform {
constructor(users: UsersProviderService, changeDetector: ChangeDetectorRef) { constructor(users: UsersProviderService, changeDetector: ChangeDetectorRef,
private readonly apiUrl: ApiUrlConfig
) {
super(users, changeDetector); super(users, changeDetector);
} }
public transform(userId: string): string | null { public transform(userId: string): string | null {
return super.transformInternal(userId, users => users.getUser(userId).map(u => u.pictureUrl)); return super.transformInternal(userId, users => users.getUser(userId).map(u => this.apiUrl.buildUrl(`api/users/${u.id}/picture`)));
} }
} }
@ -141,16 +160,18 @@ export class UserPicturePipe extends UserAsyncPipe implements PipeTransform {
pure: false pure: false
}) })
export class UserPictureRefPipe extends UserAsyncPipe implements PipeTransform { export class UserPictureRefPipe extends UserAsyncPipe implements PipeTransform {
constructor(users: UsersProviderService, changeDetector: ChangeDetectorRef) { constructor(users: UsersProviderService, changeDetector: ChangeDetectorRef,
private readonly apiUrl: ApiUrlConfig
) {
super(users, changeDetector); super(users, changeDetector);
} }
public transform(userId: string): string | null { public transform(userId: string): string | null {
return super.transformInternal(userId, users => { return super.transformInternal(userId, users => {
const parts = userId.split(':'); const parts = userId.split(':');
if (parts[0] === 'subject') { if (parts[0] === 'subject') {
return users.getUser(parts[1]).map(u => u.pictureUrl); return users.getUser(parts[1]).map(u => this.apiUrl.buildUrl(`api/users/${u.id}/picture`));
} else { } else {
return Observable.of('/images/client.png'); return Observable.of('/images/client.png');
} }

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

@ -41,6 +41,7 @@ import {
SchemasService, SchemasService,
ResolveUserGuard, ResolveUserGuard,
UsagesService, UsagesService,
UserDtoPicture,
UserEmailPipe, UserEmailPipe,
UserEmailRefPipe, UserEmailRefPipe,
UserNamePipe, UserNamePipe,
@ -66,6 +67,7 @@ import {
HelpComponent, HelpComponent,
HistoryComponent, HistoryComponent,
LanguageSelectorComponent, LanguageSelectorComponent,
UserDtoPicture,
UserEmailPipe, UserEmailPipe,
UserEmailRefPipe, UserEmailRefPipe,
UserNamePipe, UserNamePipe,
@ -80,6 +82,7 @@ import {
HelpComponent, HelpComponent,
HistoryComponent, HistoryComponent,
LanguageSelectorComponent, LanguageSelectorComponent,
UserDtoPicture,
UserEmailPipe, UserEmailPipe,
UserEmailRefPipe, UserEmailRefPipe,
UserNamePipe, UserNamePipe,

Loading…
Cancel
Save