mirror of
https://github.com/claunia/marechai.git
synced 2025-12-16 19:14:25 +00:00
Allow to add a new company logo in admin view.
This commit is contained in:
@@ -1,395 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using Marechai.Areas.Admin.Models;
|
||||
using Marechai.Database.Models;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Query;
|
||||
using SkiaSharp;
|
||||
using Svg.Skia;
|
||||
|
||||
namespace Marechai.Areas.Admin.Controllers
|
||||
{
|
||||
[Area("Admin"), Authorize]
|
||||
public class CompanyLogosController : Controller
|
||||
{
|
||||
readonly MarechaiContext _context;
|
||||
readonly IWebHostEnvironment hostingEnvironment;
|
||||
|
||||
public CompanyLogosController(MarechaiContext context, IWebHostEnvironment env)
|
||||
{
|
||||
_context = context;
|
||||
hostingEnvironment = env;
|
||||
}
|
||||
|
||||
// GET: CompanyLogos
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
IIncludableQueryable<CompanyLogo, Company> marechaiContext = _context.CompanyLogos.Include(c => c.Company);
|
||||
|
||||
return View(await marechaiContext.OrderBy(l => l.Company.Name).ThenBy(l => l.Year).
|
||||
Select(l => new CompanyLogoViewModel
|
||||
{
|
||||
Company = l.Company.Name, Id = l.Id, Year = l.Year
|
||||
}).ToListAsync());
|
||||
}
|
||||
|
||||
// GET: CompanyLogos/Details/5
|
||||
public async Task<IActionResult> Details(int? id)
|
||||
{
|
||||
if(id == null)
|
||||
return NotFound();
|
||||
|
||||
CompanyLogo companyLogo =
|
||||
await _context.CompanyLogos.Include(c => c.Company).FirstOrDefaultAsync(m => m.Id == id);
|
||||
|
||||
if(companyLogo == null)
|
||||
return NotFound();
|
||||
|
||||
return View(companyLogo);
|
||||
}
|
||||
|
||||
// GET: CompanyLogos/Create
|
||||
// TODO: Upload
|
||||
public IActionResult Create()
|
||||
{
|
||||
ViewData["CompanyId"] = new SelectList(_context.Companies.Select(c => new CompanyViewModel
|
||||
{
|
||||
Name = c.Name, Id = c.Id
|
||||
}).OrderBy(c => c.Name), "Id", "Name");
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
// POST: CompanyLogos/Create
|
||||
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
|
||||
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
|
||||
// TODO: Upload
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Create([Bind("Id,CompanyId,Year,SvgLogo")] CompanyLogo companyLogo)
|
||||
{
|
||||
if(!ModelState.IsValid)
|
||||
{
|
||||
ViewData["CompanyId"] = new SelectList(_context.Companies.Select(c => new CompanyViewModel
|
||||
{
|
||||
Name = c.Name, Id = c.Id
|
||||
}).OrderBy(c => c.Name), "Id", "Name", companyLogo.CompanyId);
|
||||
|
||||
return View(companyLogo);
|
||||
}
|
||||
|
||||
using(var svgMs = new MemoryStream())
|
||||
{
|
||||
await companyLogo.SvgLogo.CopyToAsync(svgMs);
|
||||
|
||||
svgMs.Position = 0;
|
||||
|
||||
try
|
||||
{
|
||||
var sr = new StreamReader(svgMs, Encoding.UTF8);
|
||||
string svgStr = await sr.ReadToEndAsync();
|
||||
var xml = new XmlDocument();
|
||||
xml.LoadXml(svgStr);
|
||||
}
|
||||
catch(XmlException)
|
||||
{
|
||||
companyLogo.SvgLogo = null;
|
||||
companyLogo.ErrorMessage = "Not a valid SVG file.";
|
||||
|
||||
ViewData["CompanyId"] = new SelectList(_context.Companies.Select(c => new CompanyViewModel
|
||||
{
|
||||
Name = c.Name, Id = c.Id
|
||||
}).OrderBy(c => c.Name), "Id", "Name", companyLogo.CompanyId);
|
||||
|
||||
return View(companyLogo);
|
||||
}
|
||||
|
||||
svgMs.Position = 0;
|
||||
companyLogo.Guid = Guid.NewGuid();
|
||||
|
||||
string vectorial = Path.Combine(hostingEnvironment.WebRootPath, "assets/logos",
|
||||
companyLogo.Guid + ".svg");
|
||||
|
||||
if(System.IO.File.Exists(vectorial))
|
||||
{
|
||||
companyLogo.SvgLogo = null;
|
||||
companyLogo.ErrorMessage = "GUID clash, please retry and report to the administrator.";
|
||||
|
||||
ViewData["CompanyId"] = new SelectList(_context.Companies.Select(c => new CompanyViewModel
|
||||
{
|
||||
Name = c.Name, Id = c.Id
|
||||
}).OrderBy(c => c.Name), "Id", "Name", companyLogo.CompanyId);
|
||||
|
||||
return View(companyLogo);
|
||||
}
|
||||
|
||||
var outSvg = new FileStream(vectorial, FileMode.CreateNew);
|
||||
await svgMs.CopyToAsync(outSvg);
|
||||
svgMs.Position = 0;
|
||||
|
||||
SKSvg svg = null;
|
||||
|
||||
try
|
||||
{
|
||||
foreach(string format in new[]
|
||||
{
|
||||
"png", "webp"
|
||||
})
|
||||
{
|
||||
if(!Directory.Exists(Path.Combine(hostingEnvironment.WebRootPath, "assets/logos", format)))
|
||||
Directory.CreateDirectory(Path.Combine(hostingEnvironment.WebRootPath, "assets/logos",
|
||||
format));
|
||||
|
||||
SKEncodedImageFormat skFormat;
|
||||
|
||||
switch(format)
|
||||
{
|
||||
case"webp":
|
||||
skFormat = SKEncodedImageFormat.Webp;
|
||||
|
||||
break;
|
||||
default:
|
||||
skFormat = SKEncodedImageFormat.Png;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
foreach(int multiplier in new[]
|
||||
{
|
||||
1, 2, 3
|
||||
})
|
||||
{
|
||||
if(!Directory.Exists(Path.Combine(hostingEnvironment.WebRootPath, "assets/logos", format,
|
||||
$"{multiplier}x")))
|
||||
Directory.CreateDirectory(Path.Combine(hostingEnvironment.WebRootPath, "assets/logos",
|
||||
format, $"{multiplier}x"));
|
||||
|
||||
string rendered = Path.Combine(hostingEnvironment.WebRootPath, "assets/logos", format,
|
||||
$"{multiplier}x", companyLogo.Guid + $".{format}");
|
||||
|
||||
if(System.IO.File.Exists(rendered))
|
||||
{
|
||||
companyLogo.SvgLogo = null;
|
||||
companyLogo.ErrorMessage = "GUID clash, please retry and report to the administrator.";
|
||||
|
||||
ViewData["CompanyId"] =
|
||||
new SelectList(_context.Companies.Select(c => new CompanyViewModel
|
||||
{
|
||||
Name = c.Name, Id = c.Id
|
||||
}).OrderBy(c => c.Name), "Id", "Name", companyLogo.CompanyId);
|
||||
|
||||
return View(companyLogo);
|
||||
}
|
||||
|
||||
Console.WriteLine("Rendering {0}", rendered);
|
||||
|
||||
if(svg == null)
|
||||
{
|
||||
svg = new SKSvg();
|
||||
svg.Load(svgMs);
|
||||
}
|
||||
|
||||
SKRect svgSize = svg.Picture.CullRect;
|
||||
var matrix = SKMatrix.MakeScale(multiplier, multiplier);
|
||||
|
||||
var bitmap = new SKBitmap((int)(svgSize.Width * multiplier),
|
||||
(int)(svgSize.Height * multiplier));
|
||||
|
||||
var canvas = new SKCanvas(bitmap);
|
||||
canvas.DrawPicture(svg.Picture, ref matrix);
|
||||
canvas.Flush();
|
||||
var image = SKImage.FromBitmap(bitmap);
|
||||
SKData data = image.Encode(skFormat, 100);
|
||||
var outfs = new FileStream(rendered, FileMode.CreateNew);
|
||||
data.SaveTo(outfs);
|
||||
outfs.Close();
|
||||
|
||||
svgMs.Position = 0;
|
||||
}
|
||||
}
|
||||
|
||||
foreach(string format in new[]
|
||||
{
|
||||
"png", "webp"
|
||||
})
|
||||
{
|
||||
if(!Directory.Exists(Path.Combine(hostingEnvironment.WebRootPath, "assets/logos/thumbs",
|
||||
format)))
|
||||
Directory.CreateDirectory(Path.Combine(hostingEnvironment.WebRootPath,
|
||||
"assets/logos/thumbs", format));
|
||||
|
||||
SKEncodedImageFormat skFormat;
|
||||
|
||||
switch(format)
|
||||
{
|
||||
case"webp":
|
||||
skFormat = SKEncodedImageFormat.Webp;
|
||||
|
||||
break;
|
||||
default:
|
||||
skFormat = SKEncodedImageFormat.Png;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
foreach(int multiplier in new[]
|
||||
{
|
||||
1, 2, 3
|
||||
})
|
||||
{
|
||||
if(!Directory.Exists(Path.Combine(hostingEnvironment.WebRootPath, "assets/logos/thumbs",
|
||||
format, $"{multiplier}x")))
|
||||
Directory.CreateDirectory(Path.Combine(hostingEnvironment.WebRootPath,
|
||||
"assets/logos/thumbs", format,
|
||||
$"{multiplier}x"));
|
||||
|
||||
string rendered = Path.Combine(hostingEnvironment.WebRootPath, "assets/logos/thumbs",
|
||||
format, $"{multiplier}x", companyLogo.Guid + $".{format}");
|
||||
|
||||
if(System.IO.File.Exists(rendered))
|
||||
{
|
||||
companyLogo.SvgLogo = null;
|
||||
companyLogo.ErrorMessage = "GUID clash, please retry and report to the administrator.";
|
||||
|
||||
ViewData["CompanyId"] =
|
||||
new SelectList(_context.Companies.Select(c => new CompanyViewModel
|
||||
{
|
||||
Name = c.Name, Id = c.Id
|
||||
}).OrderBy(c => c.Name), "Id", "Name", companyLogo.CompanyId);
|
||||
|
||||
return View(companyLogo);
|
||||
}
|
||||
|
||||
Console.WriteLine("Rendering {0}", rendered);
|
||||
|
||||
if(svg == null)
|
||||
{
|
||||
svg = new SKSvg();
|
||||
svg.Load(svgMs);
|
||||
}
|
||||
|
||||
SKRect svgSize = svg.Picture.CullRect;
|
||||
float svgMax = Math.Max(svgSize.Width, svgSize.Height);
|
||||
float canvasMin = 32 * multiplier;
|
||||
float scale = canvasMin / svgMax;
|
||||
var matrix = SKMatrix.MakeScale(scale, scale);
|
||||
|
||||
var bitmap = new SKBitmap((int)(svgSize.Width * scale), (int)(svgSize.Height * scale));
|
||||
|
||||
var canvas = new SKCanvas(bitmap);
|
||||
canvas.DrawPicture(svg.Picture, ref matrix);
|
||||
canvas.Flush();
|
||||
var image = SKImage.FromBitmap(bitmap);
|
||||
SKData data = image.Encode(skFormat, 100);
|
||||
var outfs = new FileStream(rendered, FileMode.CreateNew);
|
||||
data.SaveTo(outfs);
|
||||
outfs.Close();
|
||||
|
||||
svgMs.Position = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
_context.Add(companyLogo);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
// GET: CompanyLogos/Edit/5
|
||||
public async Task<IActionResult> Edit(int? id)
|
||||
{
|
||||
if(id == null)
|
||||
return NotFound();
|
||||
|
||||
CompanyLogo companyLogo = await _context.CompanyLogos.FirstOrDefaultAsync(c => c.Id == id);
|
||||
|
||||
if(companyLogo == null)
|
||||
return NotFound();
|
||||
|
||||
ViewData["CompanyId"] =
|
||||
new SelectList(_context.Companies.OrderBy(l => l.Name), "Id", "Name", companyLogo.CompanyId);
|
||||
|
||||
return View(companyLogo);
|
||||
}
|
||||
|
||||
// POST: CompanyLogos/Edit/5
|
||||
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
|
||||
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Edit(int id, [Bind("Id,CompanyId,Year,Guid")] CompanyLogo companyLogo)
|
||||
{
|
||||
if(id != companyLogo.Id)
|
||||
return NotFound();
|
||||
|
||||
if(ModelState.IsValid)
|
||||
{
|
||||
try
|
||||
{
|
||||
_context.Update(companyLogo);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
catch(DbUpdateConcurrencyException)
|
||||
{
|
||||
if(!CompanyLogoExists(companyLogo.Id))
|
||||
return NotFound();
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
ViewData["CompanyId"] =
|
||||
new SelectList(_context.Companies.OrderBy(l => l.Name), "Id", "Name", companyLogo.CompanyId);
|
||||
|
||||
return View(companyLogo);
|
||||
}
|
||||
|
||||
// GET: CompanyLogos/Delete/5
|
||||
public async Task<IActionResult> Delete(int? id)
|
||||
{
|
||||
if(id == null)
|
||||
return NotFound();
|
||||
|
||||
CompanyLogo companyLogo =
|
||||
await _context.CompanyLogos.Include(c => c.Company).FirstOrDefaultAsync(m => m.Id == id);
|
||||
|
||||
if(companyLogo == null)
|
||||
return NotFound();
|
||||
|
||||
return View(companyLogo);
|
||||
}
|
||||
|
||||
// POST: CompanyLogos/Delete/5
|
||||
[HttpPost, ActionName("Delete"), ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> DeleteConfirmed(int id)
|
||||
{
|
||||
CompanyLogo companyLogo = await _context.CompanyLogos.FirstOrDefaultAsync(m => m.Id == id);
|
||||
|
||||
if(companyLogo == null)
|
||||
return NotFound();
|
||||
|
||||
_context.CompanyLogos.Remove(companyLogo);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
bool CompanyLogoExists(int id) => _context.CompanyLogos.Any(e => e.Id == id);
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
@model Marechai.Database.Models.CompanyLogo
|
||||
@{
|
||||
ViewData["Title"] = "Create";
|
||||
}
|
||||
<h1>Create</h1>
|
||||
<h4>Company logo</h4>
|
||||
<hr />
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<form asp-action="Create" enctype="multipart/form-data">
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Company" class="control-label">
|
||||
</label>
|
||||
<select asp-for="CompanyId" class="form-control" asp-items="ViewBag.CompanyId">
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Year" class="control-label">
|
||||
</label>
|
||||
<input asp-for="Year" class="form-control" />
|
||||
<span asp-validation-for="Year" class="text-danger">
|
||||
</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-10">
|
||||
<label asp-for="SvgLogo" class="control-label">
|
||||
</label>
|
||||
<input asp-for="SvgLogo" class="form-control" />
|
||||
<span asp-validation-for="SvgLogo" class="text-danger">
|
||||
</span>
|
||||
@if (Model?.ErrorMessage != null)
|
||||
{
|
||||
<span class="text-danger">@Model.ErrorMessage</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input class="btn btn-primary" type="submit" value="Create" />
|
||||
<a asp-action="Index" class="btn btn-secondary">
|
||||
Back to List
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||
}
|
||||
@@ -95,20 +95,8 @@ namespace Marechai.Helpers
|
||||
svg.Load(file);
|
||||
}
|
||||
|
||||
SKRect svgSize = svg.Picture.CullRect;
|
||||
float svgMax = Math.Max(svgSize.Width, svgSize.Height);
|
||||
float canvasMin = 32 * multiplier;
|
||||
float scale = canvasMin / svgMax;
|
||||
var matrix = SKMatrix.MakeScale(scale, scale);
|
||||
var bitmap = new SKBitmap((int)(svgSize.Width * scale), (int)(svgSize.Height * scale));
|
||||
var canvas = new SKCanvas(bitmap);
|
||||
canvas.Clear();
|
||||
canvas.DrawPicture(svg.Picture, ref matrix);
|
||||
canvas.Flush();
|
||||
var image = SKImage.FromBitmap(bitmap);
|
||||
SKData data = image.Encode(skFormat, 100);
|
||||
var outFs = new FileStream(rendered, FileMode.CreateNew);
|
||||
data.SaveTo(outFs);
|
||||
var outFs = new FileStream(rendered, FileMode.CreateNew);
|
||||
RenderSvg(svg, outFs, skFormat, 32, multiplier);
|
||||
outFs.Close();
|
||||
}
|
||||
}
|
||||
@@ -210,20 +198,8 @@ namespace Marechai.Helpers
|
||||
svg.Load(file);
|
||||
}
|
||||
|
||||
SKRect svgSize = svg.Picture.CullRect;
|
||||
float svgMax = Math.Max(svgSize.Width, svgSize.Height);
|
||||
float canvasMin = minSize * multiplier;
|
||||
float scale = canvasMin / svgMax;
|
||||
var matrix = SKMatrix.MakeScale(scale, scale);
|
||||
var bitmap = new SKBitmap((int)(svgSize.Width * scale), (int)(svgSize.Height * scale));
|
||||
var canvas = new SKCanvas(bitmap);
|
||||
canvas.Clear();
|
||||
canvas.DrawPicture(svg.Picture, ref matrix);
|
||||
canvas.Flush();
|
||||
var image = SKImage.FromBitmap(bitmap);
|
||||
SKData data = image.Encode(skFormat, 100);
|
||||
var outFs = new FileStream(rendered, FileMode.CreateNew);
|
||||
data.SaveTo(outFs);
|
||||
var outFs = new FileStream(rendered, FileMode.CreateNew);
|
||||
RenderSvg(svg, outFs, skFormat, minSize, multiplier);
|
||||
outFs.Close();
|
||||
}
|
||||
}
|
||||
@@ -232,5 +208,89 @@ namespace Marechai.Helpers
|
||||
File.Move(file, $"wwwroot/assets/logos/{guid}.svg");
|
||||
}
|
||||
}
|
||||
|
||||
public static void RenderSvg(SKSvg svg, Stream outStream, SKEncodedImageFormat skFormat, int minSize,
|
||||
int multiplier)
|
||||
{
|
||||
SKRect svgSize = svg.Picture.CullRect;
|
||||
float svgMax = Math.Max(svgSize.Width, svgSize.Height);
|
||||
float canvasMin = minSize * multiplier;
|
||||
float scale = canvasMin / svgMax;
|
||||
var matrix = SKMatrix.MakeScale(scale, scale);
|
||||
var bitmap = new SKBitmap((int)(svgSize.Width * scale), (int)(svgSize.Height * scale));
|
||||
var canvas = new SKCanvas(bitmap);
|
||||
canvas.Clear();
|
||||
canvas.DrawPicture(svg.Picture, ref matrix);
|
||||
canvas.Flush();
|
||||
var image = SKImage.FromBitmap(bitmap);
|
||||
SKData data = image.Encode(skFormat, 100);
|
||||
data.SaveTo(outStream);
|
||||
}
|
||||
|
||||
// TODO: Reduce code duplication
|
||||
public static void RenderCompanyLogo(Guid guid, Stream svgStream, string wwwroot)
|
||||
{
|
||||
SKSvg svg = null;
|
||||
|
||||
foreach(int minSize in new[]
|
||||
{
|
||||
256, 32
|
||||
})
|
||||
{
|
||||
foreach(string format in new[]
|
||||
{
|
||||
"png", "webp"
|
||||
})
|
||||
{
|
||||
string outDir = minSize == 32 ? Path.Combine(wwwroot, "assets/logos/thumbs", format)
|
||||
: Path.Combine(wwwroot, "assets/logos", format);
|
||||
|
||||
if(!Directory.Exists(outDir))
|
||||
Directory.CreateDirectory(outDir);
|
||||
|
||||
SKEncodedImageFormat skFormat;
|
||||
|
||||
switch(format)
|
||||
{
|
||||
case"webp":
|
||||
skFormat = SKEncodedImageFormat.Webp;
|
||||
|
||||
break;
|
||||
default:
|
||||
skFormat = SKEncodedImageFormat.Png;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
foreach(int multiplier in new[]
|
||||
{
|
||||
1, 2, 3
|
||||
})
|
||||
{
|
||||
string outPath = Path.Combine(outDir, $"{multiplier}x");
|
||||
|
||||
if(!Directory.Exists(outPath))
|
||||
Directory.CreateDirectory(outPath);
|
||||
|
||||
string rendered = Path.Combine(outPath, $"{guid}.{format}");
|
||||
|
||||
if(File.Exists(rendered))
|
||||
continue;
|
||||
|
||||
Console.WriteLine("Rendering {0}", rendered);
|
||||
|
||||
if(svg == null)
|
||||
{
|
||||
svg = new SKSvg();
|
||||
svg.Load(svgStream);
|
||||
}
|
||||
|
||||
var outFs = new FileStream(rendered, FileMode.CreateNew);
|
||||
RenderSvg(svg, outFs, skFormat, minSize, multiplier);
|
||||
outFs.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<Version>3.0.99.1275</Version>
|
||||
<Version>3.0.99.1364</Version>
|
||||
<Company>Canary Islands Computer Museum</Company>
|
||||
<Copyright>Copyright © 2003-2020 Natalia Portillo</Copyright>
|
||||
<Product>Canary Islands Computer Museum Website</Product>
|
||||
@@ -31,6 +31,7 @@
|
||||
<PackageReference Include="SkiaSharp.Extended" Version="1.60.0" />
|
||||
<PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="1.68.3" />
|
||||
<PackageReference Include="Svg.Skia" Version="0.3.0" />
|
||||
<PackageReference Include="Tewr.Blazor.FileReader" Version="1.5.0.20109" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.3.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -142,5 +143,6 @@
|
||||
<_ContentIncludedByDefault Remove="Areas\Admin\Views\Processors\Create.cshtml" />
|
||||
<_ContentIncludedByDefault Remove="Areas\Admin\Views\Screens\Create.cshtml" />
|
||||
<_ContentIncludedByDefault Remove="Areas\Admin\Views\SoundSynths\Create.cshtml" />
|
||||
<_ContentIncludedByDefault Remove="Areas\Admin\Views\CompanyLogos\Create.cshtml" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -40,7 +40,10 @@
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject CompanyLogosService CompanyLogosService
|
||||
@inject IWebHostEnvironment Host
|
||||
@inject IFileReaderService FileReaderService;
|
||||
@attribute [Authorize(Roles = "UberAdmin, Admin")]
|
||||
|
||||
|
||||
<h3>@L["Company details"]</h3>
|
||||
<hr />
|
||||
|
||||
@@ -316,7 +319,7 @@
|
||||
<a href="/admin/companies" class="btn btn-secondary">@L["Back to list"]</a>
|
||||
@if (!_editing)
|
||||
{
|
||||
<a class="btn btn-success">@L["Upload new logo"]</a>
|
||||
<Button Color="Color.Success" Clicked="@ShowUploadModal">@L["Upload new logo"]</Button>
|
||||
}
|
||||
</div>
|
||||
@if (_logos.Count > 0)
|
||||
@@ -385,7 +388,14 @@
|
||||
<CloseButton Clicked="@HideDeleteModal"/>
|
||||
</ModalHeader>
|
||||
<ModalBody>
|
||||
<Text>@string.Format(@L["Are you sure you want to delete the company logo introduced in {0}?"], _currentLogo?.Year)</Text>
|
||||
@if (_currentLogo?.Year != null)
|
||||
{
|
||||
<Text>@string.Format(L["Are you sure you want to delete the company logo introduced in {0}?"], _currentLogo?.Year)</Text>
|
||||
}
|
||||
else
|
||||
{
|
||||
<Text>@L["Are you sure you want to delete the company logo you selected?"]</Text>
|
||||
}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button Color="Color.Primary" Clicked="@HideDeleteModal" Disabled="@_deleteInProgress">@L["Cancel"]</Button>
|
||||
@@ -436,3 +446,89 @@
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
||||
<Modal @ref="_frmUpload" IsCentered="true" Closing="@UploadModalClosing">
|
||||
<ModalBackdrop/>
|
||||
<ModalContent Centered="true" Size="ModalSize.ExtraLarge">
|
||||
<ModalHeader>
|
||||
<ModalTitle>@L["Upload new logo"]</ModalTitle>
|
||||
<CloseButton Clicked="@HideUploadModal"/>
|
||||
</ModalHeader>
|
||||
<ModalBody>
|
||||
<Field>
|
||||
@if (!_uploaded)
|
||||
{
|
||||
@if (!_uploading)
|
||||
{
|
||||
<FieldLabel>@L["Choose SVG (version 1.1 or lower) logo file"]</FieldLabel>
|
||||
<input type="file" @ref=_inputUpload Accept=".svg" /><br/>
|
||||
<Button Color="Color.Success" Clicked="@UploadFile" Disabled="@_uploading">@L["Upload"]</Button><br/>
|
||||
}
|
||||
else
|
||||
{
|
||||
@L["Uploading..."]
|
||||
<div class="progress">
|
||||
<div class="progress-bar" role="progressbar" style="width: @(_progressValue)%;" aria-valuenow="@_progressValue" aria-valuemin="0" aria-valuemax="100">@($"{_progressValue:F2}")%</div>
|
||||
</div>
|
||||
}
|
||||
@if (_uploadError)
|
||||
{
|
||||
<span class="text-danger">@_uploadErrorMessage</span>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<table class="table table-dark">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>@L["SVG"]</th>
|
||||
<th>@L["PNG"]</th>
|
||||
<th>@L["WebP"]</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<span class="text-warning">@L["May not show properly"]</span>
|
||||
</td>
|
||||
<td></td>
|
||||
<td>
|
||||
<span class="text-warning">@L["May not show properly"]</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<img src="@_uploadedSvgData" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
|
||||
</td>
|
||||
<td>
|
||||
<img src="@_uploadedPngData" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
|
||||
</td>
|
||||
<td>
|
||||
<img src="@_uploadedWebpData" alt="" height="auto" width="auto" style="max-height: 256px; max-width: 256px" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<Field>
|
||||
<FieldLabel>@L["Year logo came in use"]</FieldLabel>
|
||||
<Check TValue="bool" @bind-Checked="@_unknownLogoYear">@L["Unknown (logo year)"]</Check>
|
||||
@if (!_unknownLogoYear)
|
||||
{
|
||||
<Validation Validator="@ValidateLogoYear">
|
||||
<NumericEdit TValue="int?" Decimals="0" @bind-Value="@_currentLogoYear">
|
||||
<Feedback>
|
||||
<ValidationError>@L["Please enter a valid year."]</ValidationError>
|
||||
</Feedback>
|
||||
</NumericEdit>
|
||||
</Validation>
|
||||
}
|
||||
</Field>
|
||||
}
|
||||
</Field>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button Color="Color.Primary" Clicked="@HideUploadModal" Disabled="_uploading || _savingLogo">@L["Cancel"]</Button>
|
||||
<Button Color="Color.Success" Clicked="@ConfirmUpload" Disabled="!_uploaded || _savingLogo">@L["Save"]</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
@@ -1,18 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Blazor.FileReader;
|
||||
using Blazorise;
|
||||
using Marechai.Database;
|
||||
using Marechai.Database.Models;
|
||||
using Marechai.Helpers;
|
||||
using Marechai.Shared;
|
||||
using Marechai.ViewModels;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using SkiaSharp;
|
||||
using Svg.Skia;
|
||||
|
||||
namespace Marechai.Pages.Admin.Details
|
||||
{
|
||||
public partial class Company
|
||||
{
|
||||
const int _maxUploadSize = 5 * 1048576;
|
||||
List<CompanyViewModel> _companies;
|
||||
List<Iso31661Numeric> _countries;
|
||||
bool _creating;
|
||||
@@ -22,23 +29,35 @@ namespace Marechai.Pages.Admin.Details
|
||||
bool _editing;
|
||||
Modal _frmDelete;
|
||||
Modal _frmLogoYear;
|
||||
Modal _frmUpload;
|
||||
ElementReference _inputUpload;
|
||||
bool _loaded;
|
||||
List<CompanyLogo> _logos;
|
||||
CompanyViewModel _model;
|
||||
bool _unknownAddress;
|
||||
bool _unknownCity;
|
||||
bool _unknownCountry;
|
||||
bool _unknownFacebook;
|
||||
bool _unknownFounded;
|
||||
bool _unknownLogoYear;
|
||||
bool _unknownPostalCode;
|
||||
bool _unknownProvince;
|
||||
bool _unknownSold;
|
||||
bool _unknownSoldTo;
|
||||
bool _unknownTwitter;
|
||||
bool _unknownWebsite;
|
||||
double _progressValue;
|
||||
|
||||
bool _yearChangeInProgress;
|
||||
bool _savingLogo;
|
||||
bool _unknownAddress;
|
||||
bool _unknownCity;
|
||||
bool _unknownCountry;
|
||||
bool _unknownFacebook;
|
||||
bool _unknownFounded;
|
||||
bool _unknownLogoYear;
|
||||
bool _unknownPostalCode;
|
||||
bool _unknownProvince;
|
||||
bool _unknownSold;
|
||||
bool _unknownSoldTo;
|
||||
bool _unknownTwitter;
|
||||
bool _unknownWebsite;
|
||||
bool _uploaded;
|
||||
string _uploadedPngData;
|
||||
string _uploadedSvgData;
|
||||
string _uploadedWebpData;
|
||||
bool _uploadError;
|
||||
string _uploadErrorMessage;
|
||||
bool _uploading;
|
||||
MemoryStream _uploadMs;
|
||||
bool _yearChangeInProgress;
|
||||
[Parameter]
|
||||
public int Id { get; set; }
|
||||
|
||||
@@ -323,5 +342,216 @@ namespace Marechai.Pages.Admin.Details
|
||||
else
|
||||
e.Status = ValidationStatus.Success;
|
||||
}
|
||||
|
||||
void ShowUploadModal()
|
||||
{
|
||||
_uploadError = false;
|
||||
_uploadErrorMessage = "";
|
||||
_uploading = false;
|
||||
_uploadMs = null;
|
||||
_uploaded = false;
|
||||
_progressValue = 0;
|
||||
_uploadedSvgData = "";
|
||||
_uploadedPngData = "";
|
||||
_uploadedWebpData = "";
|
||||
_savingLogo = false;
|
||||
_frmUpload.Show();
|
||||
}
|
||||
|
||||
void HideUploadModal() => _frmUpload.Hide();
|
||||
|
||||
void UploadModalClosing(ModalClosingEventArgs e)
|
||||
{
|
||||
_uploadError = false;
|
||||
_uploadErrorMessage = "";
|
||||
_uploading = false;
|
||||
_uploadMs = null;
|
||||
_uploaded = false;
|
||||
_progressValue = 0;
|
||||
_uploadedSvgData = "";
|
||||
_uploadedPngData = "";
|
||||
_uploadedWebpData = "";
|
||||
_savingLogo = false;
|
||||
}
|
||||
|
||||
async Task UploadFile()
|
||||
{
|
||||
IFileReference file = (await FileReaderService.CreateReference(_inputUpload).EnumerateFilesAsync()).
|
||||
FirstOrDefault();
|
||||
|
||||
if(file is null)
|
||||
return;
|
||||
|
||||
IFileInfo fileInfo = await file.ReadFileInfoAsync();
|
||||
|
||||
if(fileInfo.Size > _maxUploadSize)
|
||||
{
|
||||
_uploadError = true;
|
||||
_uploadErrorMessage = L["The selected file is too big."];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_uploading = true;
|
||||
|
||||
await using AsyncDisposableStream fs = await file.OpenReadAsync();
|
||||
byte[] buffer = new byte[20480];
|
||||
|
||||
try
|
||||
{
|
||||
double lastProgress = 0;
|
||||
int count;
|
||||
_uploadMs = new MemoryStream();
|
||||
|
||||
while((count = await fs.ReadAsync(buffer, 0, buffer.Length)) != 0)
|
||||
{
|
||||
await _uploadMs.WriteAsync(buffer, 0, count);
|
||||
|
||||
double progress = ((double)fs.Position * 100) / fs.Length;
|
||||
|
||||
if(!(progress > lastProgress + 0.01))
|
||||
continue;
|
||||
|
||||
_progressValue = progress;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
await Task.Yield();
|
||||
lastProgress = progress;
|
||||
}
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
_uploading = false;
|
||||
_uploadError = true;
|
||||
_uploadErrorMessage = L["There was an error uploading the file."];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_uploading = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
await Task.Yield();
|
||||
|
||||
bool validSvg = true;
|
||||
|
||||
buffer = new byte[6];
|
||||
|
||||
_uploadMs.Position = 0;
|
||||
_uploadMs.Read(buffer, 0, 6);
|
||||
|
||||
if(!Encoding.UTF8.GetString(buffer).StartsWith("<?xml", StringComparison.InvariantCulture) &&
|
||||
!Encoding.UTF8.GetString(buffer).StartsWith("<svg", StringComparison.InvariantCulture))
|
||||
validSvg = false;
|
||||
|
||||
_uploadMs.Seek(-6, SeekOrigin.End);
|
||||
_uploadMs.Read(buffer, 0, 6);
|
||||
|
||||
// LF
|
||||
if(buffer[^1] == 0x0A)
|
||||
{
|
||||
_uploadMs.Seek(-7, SeekOrigin.End);
|
||||
_uploadMs.Read(buffer, 0, 6);
|
||||
}
|
||||
|
||||
// CR
|
||||
if(buffer[^1] == 0x0D)
|
||||
{
|
||||
_uploadMs.Seek(-8, SeekOrigin.End);
|
||||
_uploadMs.Read(buffer, 0, 6);
|
||||
}
|
||||
|
||||
if(!Encoding.UTF8.GetString(buffer).StartsWith("</svg>", StringComparison.InvariantCulture))
|
||||
validSvg = false;
|
||||
|
||||
if(!validSvg)
|
||||
{
|
||||
_uploadMs = null;
|
||||
_uploadError = true;
|
||||
_uploadErrorMessage = L["The uploaded file is not a SVG file."];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_uploadMs.Position = 0;
|
||||
|
||||
var svg = new SKSvg();
|
||||
|
||||
try
|
||||
{
|
||||
svg.Load(_uploadMs);
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
_uploadMs = null;
|
||||
_uploadError = true;
|
||||
_uploadErrorMessage = L["The uploaded file could not be loaded. Is it a correct SVG file?"];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var pngStream = new MemoryStream();
|
||||
var webpStream = new MemoryStream();
|
||||
|
||||
try
|
||||
{
|
||||
SvgRender.RenderSvg(svg, pngStream, SKEncodedImageFormat.Png, 256, 1);
|
||||
SvgRender.RenderSvg(svg, webpStream, SKEncodedImageFormat.Webp, 256, 1);
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
_uploadMs = null;
|
||||
_uploadError = true;
|
||||
_uploadErrorMessage = L["An error occuring rendering the uploaded file. Is it a correct SVG file?"];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_uploadMs.Position = 0;
|
||||
pngStream.Position = 0;
|
||||
webpStream.Position = 0;
|
||||
|
||||
_uploadedSvgData = $"data:image/svg+xml;base64,{Convert.ToBase64String(_uploadMs.ToArray())}";
|
||||
_uploadedPngData = $"data:image/png;base64,{Convert.ToBase64String(pngStream.ToArray())}";
|
||||
_uploadedWebpData = $"data:image/webp;base64,{Convert.ToBase64String(webpStream.ToArray())}";
|
||||
|
||||
_unknownLogoYear = true;
|
||||
_uploaded = true;
|
||||
}
|
||||
|
||||
async Task ConfirmUpload()
|
||||
{
|
||||
_savingLogo = true;
|
||||
_uploadMs.Position = 0;
|
||||
var guid = Guid.NewGuid();
|
||||
|
||||
try
|
||||
{
|
||||
SvgRender.RenderCompanyLogo(guid, _uploadMs, Host.WebRootPath);
|
||||
|
||||
var fs = new FileStream(Path.Combine(Host.WebRootPath, "assets/logos", $"{guid}.svg"), FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
||||
_uploadMs.Position = 0;
|
||||
_uploadMs.WriteTo(fs);
|
||||
fs.Close();
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
_savingLogo = false;
|
||||
_uploadError = true;
|
||||
_uploadErrorMessage = L["An error occuring rendering the uploaded file. Is it a correct SVG file?"];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await CompanyLogosService.CreateAsync(Id, guid, _unknownLogoYear ? null : _currentLogoYear);
|
||||
|
||||
_logos = await CompanyLogosService.GetByCompany(Id);
|
||||
|
||||
_frmUpload.Hide();
|
||||
|
||||
// Yield thread to let UI to update
|
||||
await Task.Yield();
|
||||
|
||||
// Tell we finished loading
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -446,4 +446,56 @@
|
||||
<value>Por favor introduce un año válido.</value>
|
||||
<comment>Please enter a valid year.</comment>
|
||||
</data>
|
||||
<data name="Are you sure you want to delete the company logo you selected?" xml:space="preserve">
|
||||
<value>¿Estás seguro de que quieres borrar el logo de la compañía que has seleccionado?</value>
|
||||
<comment>Are you sure you want to delete the company logo you selected?</comment>
|
||||
</data>
|
||||
<data name="Choose SVG (version 1.1 or lower) logo file" xml:space="preserve">
|
||||
<value>Elige un archivo con el logo en format SVG (versión 1.1 o inferior)</value>
|
||||
<comment>Choose SVG (version 1.1 or lower) logo file</comment>
|
||||
</data>
|
||||
<data name="Upload" xml:space="preserve">
|
||||
<value>Subir</value>
|
||||
<comment>Upload</comment>
|
||||
</data>
|
||||
<data name="Uploading..." xml:space="preserve">
|
||||
<value>Subiendo...</value>
|
||||
<comment>Uploading...</comment>
|
||||
</data>
|
||||
<data name="SVG" xml:space="preserve">
|
||||
<value>SVG</value>
|
||||
<comment>SVG</comment>
|
||||
</data>
|
||||
<data name="PNG" xml:space="preserve">
|
||||
<value>PNG</value>
|
||||
<comment>PNG</comment>
|
||||
</data>
|
||||
<data name="WebP" xml:space="preserve">
|
||||
<value>WebP</value>
|
||||
<comment>WebP</comment>
|
||||
</data>
|
||||
<data name="May not show properly" xml:space="preserve">
|
||||
<value>Puede que no se muestre correctamente</value>
|
||||
<comment>May not show properly</comment>
|
||||
</data>
|
||||
<data name="The selected file is too big." xml:space="preserve">
|
||||
<value>El archivo seleccionado es demasiado grande.</value>
|
||||
<comment>The selected file is too big.</comment>
|
||||
</data>
|
||||
<data name="There was an error uploading the file." xml:space="preserve">
|
||||
<value>Hubo un error subiendo el archivo.</value>
|
||||
<comment>There was an error uploading the file.</comment>
|
||||
</data>
|
||||
<data name="The uploaded file is not a SVG file." xml:space="preserve">
|
||||
<value>El archivo que has subido no es un archivo SVG.</value>
|
||||
<comment>The uploaded file is not a SVG file.</comment>
|
||||
</data>
|
||||
<data name="The uploaded file could not be loaded. Is it a correct SVG file?" xml:space="preserve">
|
||||
<value>No se pudo cargar el archivo que has subido. ¿Es un SVG correcto?</value>
|
||||
<comment>The uploaded file could not be loaded. Is it a correct SVG file?</comment>
|
||||
</data>
|
||||
<data name="An error occuring rendering the uploaded file. Is it a correct SVG file?" xml:space="preserve">
|
||||
<value>Ocurrió un error renderizando el archivo subido. ¿Es un SVG correcto?</value>
|
||||
<comment>An error occuring rendering the uploaded file. Is it a correct SVG file?</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -84,5 +85,18 @@ namespace Marechai.Services
|
||||
logo.Year = year;
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<int> CreateAsync(int companyId, Guid guid, int? year)
|
||||
{
|
||||
var logo = new CompanyLogo
|
||||
{
|
||||
Guid = guid, Year = year, CompanyId = companyId
|
||||
};
|
||||
|
||||
await _context.CompanyLogos.AddAsync(logo);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return logo.Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@
|
||||
*******************************************************************************/
|
||||
|
||||
using System.Globalization;
|
||||
using Blazor.FileReader;
|
||||
using Blazorise;
|
||||
using Blazorise.Bootstrap;
|
||||
using Blazorise.Icons.FontAwesome;
|
||||
@@ -83,6 +84,8 @@ namespace Marechai
|
||||
|
||||
services.AddLocalization(options => options.ResourcesPath = "Resources");
|
||||
|
||||
services.AddFileReaderService();
|
||||
|
||||
Register.RegisterServices(services);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,4 +12,5 @@
|
||||
@using Microsoft.Extensions.Localization
|
||||
@using System.IO
|
||||
@using Microsoft.AspNetCore.Hosting
|
||||
@using Blazorise
|
||||
@using Blazorise
|
||||
@using Blazor.FileReader
|
||||
|
||||
Reference in New Issue
Block a user