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.
// ==========================================================================
using System;
using System.Linq;
using Squidex.Core.Identity;
using Squidex.Infrastructure;
// ReSharper disable InvertIf
namespace Squidex.Read.Users
{
public static class UserExtensions
@ -38,5 +41,24 @@ namespace Squidex.Read.Users
{
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.
// ==========================================================================
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
@ -17,18 +20,32 @@ using Squidex.Infrastructure.Reflection;
using Squidex.Pipeline;
using Squidex.Read.Users;
// ReSharper disable InvertIf
namespace Squidex.Controllers.Api.Users
{
/// <summary>
/// Readonly API to retrieve information about squidex users.
/// </summary>
[Authorize]
[ApiExceptionFilter]
[SwaggerTag("Users")]
public class UsersController : Controller
{
private static readonly byte[] AvatarBytes;
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)
{
this.userManager = userManager;
@ -44,6 +61,7 @@ namespace Squidex.Controllers.Api.Users
/// <returns>
/// 200 => Users returned.
/// </returns>
[Authorize]
[HttpGet]
[Route("users")]
[ProducesResponseType(typeof(UserDto[]), 200)]
@ -64,6 +82,7 @@ namespace Squidex.Controllers.Api.Users
/// 200 => User found.
/// 404 => User not found.
/// </returns>
[Authorize]
[HttpGet]
[Route("users/{id}/")]
[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() });
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>
<EmbeddedResource Include="Config\Identity\Cert\*.*;Docs\*.md" />
<EmbeddedResource Include="Controllers\Api\Users\Assets\Avatar.png" />
<EmbeddedResource Remove="Assets\**" />
<Compile 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">
<tr [routerLink]="user.id" routerLinkActive="active">
<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>
<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 { Observable, Subscription } from 'rxjs';
import { UsersProviderService } from './../declarations-base';
import { ApiUrlConfig } from 'framework';
import { UserDto, UsersProviderService } from './../declarations-base';
class UserAsyncPipe implements OnDestroy {
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({
name: 'userPicture',
pure: false
})
export class UserPicturePipe extends UserAsyncPipe implements PipeTransform {
constructor(users: UsersProviderService, changeDetector: ChangeDetectorRef) {
constructor(users: UsersProviderService, changeDetector: ChangeDetectorRef,
private readonly apiUrl: ApiUrlConfig
) {
super(users, changeDetector);
}
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
})
export class UserPictureRefPipe extends UserAsyncPipe implements PipeTransform {
constructor(users: UsersProviderService, changeDetector: ChangeDetectorRef) {
constructor(users: UsersProviderService, changeDetector: ChangeDetectorRef,
private readonly apiUrl: ApiUrlConfig
) {
super(users, changeDetector);
}
public transform(userId: string): string | null {
return super.transformInternal(userId, users => {
const parts = userId.split(':');
const parts = userId.split(':');
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 {
return Observable.of('/images/client.png');
}

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

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

Loading…
Cancel
Save