Skip to content
This repository was archived by the owner on Nov 23, 2024. It is now read-only.

Commit 42f7891

Browse files
committed
Implement profile and curriculum upload
1 parent ba2522e commit 42f7891

13 files changed

+232
-15
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@ secret.env
77
composer.phar
88
/src/DonationManager/vendor/*
99
*.xlsx
10+
data/curricula/*
11+
!data/curricula/placeholder
12+
data/profile/*
13+
!data/profile/placeholder

data/curricula/placeholder

Whitespace-only changes.

data/profile/placeholder

Whitespace-only changes.

docker-compose.yml

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ services:
1212
networks:
1313
- web
1414
- codemooc-reg-backend
15+
volumes:
16+
- ./data:/data:rw
1517
env_file:
1618
- config.env
1719
- secret.env

src/Web/Dockerfile

-3
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,4 @@ COPY --from=build /app/Web/out ./
2323
ENV ASPNETCORE_URLS http://+:5001
2424
EXPOSE 5001
2525

26-
# Drop privileges
27-
USER 1000
28-
2926
ENTRYPOINT ["dotnet", "CodeMooc.Web.dll"]

src/Web/Web/Controllers/DashboardController.cs

+100-10
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.IO;
34
using System.Linq;
45
using System.Security.Claims;
56
using System.Threading.Tasks;
67
using CodeMooc.Web.Data;
78
using CodeMooc.Web.Model;
89
using Microsoft.AspNetCore.Authentication.Cookies;
910
using Microsoft.AspNetCore.Authorization;
11+
using Microsoft.AspNetCore.Http;
1012
using Microsoft.AspNetCore.Mvc;
1113
using Microsoft.EntityFrameworkCore;
1214
using Microsoft.Extensions.Configuration;
1315
using Microsoft.Extensions.Logging;
16+
using SixLabors.ImageSharp;
17+
using SixLabors.ImageSharp.PixelFormats;
18+
using SixLabors.ImageSharp.Processing;
1419

1520
namespace CodeMooc.Web.Controllers {
1621

@@ -32,10 +37,7 @@ IConfiguration configuration
3237
Configuration = configuration;
3338
}
3439

35-
protected T GetViewModel<T>()
36-
where T : DashboardBaseViewModel {
37-
T model = (T)Activator.CreateInstance(typeof(T));
38-
40+
protected int? GetUserId() {
3941
if(!HttpContext.User.Identity.IsAuthenticated) {
4042
Logger.LogError("User not authenticated on dashboard");
4143
return null;
@@ -47,8 +49,21 @@ protected T GetViewModel<T>()
4749
return null;
4850
}
4951

52+
return id;
53+
}
54+
55+
protected T GetViewModel<T>()
56+
where T : DashboardBaseViewModel {
57+
58+
T model = (T)Activator.CreateInstance(typeof(T));
59+
60+
var userId = GetUserId();
61+
if(!userId.HasValue) {
62+
return null;
63+
}
64+
5065
model.LoggedUser = (from r in Database.Registrations
51-
where r.Id == id
66+
where r.Id == userId.Value
5267
select r).SingleOrDefault();
5368
Logger.LogDebug("Loaded user information for user {0} {1}", model.LoggedUser.Id, model.LoggedUser.Name);
5469

@@ -108,14 +123,89 @@ public IActionResult ShowAssociationStatus() {
108123
return View("Status", model);
109124
}
110125

111-
[HttpGet("cv-upload")]
126+
protected string GetCurriculumPath(int userId) {
127+
var pathsConf = Configuration.GetSection("Paths");
128+
string pathCurricula = pathsConf["Curricula"];
129+
130+
return Path.Combine(pathCurricula, $"{userId}.pdf");
131+
}
132+
133+
protected string GetProfilePicPath(int userId) {
134+
var pathsConf = Configuration.GetSection("Paths");
135+
string pathProfilePics = pathsConf["ProfilePics"];
136+
137+
return Path.Combine(pathProfilePics, $"{userId}.jpg");
138+
}
139+
140+
[HttpGet("curriculum")]
112141
public IActionResult ShowCvUpload() {
113-
return null;
142+
var model = GetViewModel<DashboardUploadViewModel>();
143+
var userId = model.LoggedUser.Id;
144+
145+
var pathProfile = GetProfilePicPath(userId);
146+
model.ProfilePictureFilename = System.IO.File.Exists(pathProfile) ? Path.GetFileName(pathProfile) : null;
147+
Logger.LogDebug("Path {0} exists {1}", pathProfile, model.ProfilePictureFilename != null);
148+
149+
var pathCurriculum = GetCurriculumPath(userId);
150+
model.CurriculumFilename = System.IO.File.Exists(pathCurriculum) ? Path.GetFileName(pathCurriculum) : null;
151+
Logger.LogDebug("Path {0} exists {1}", pathCurriculum, model.ProfilePictureFilename != null);
152+
153+
return View("Upload", model);
154+
}
155+
156+
[HttpPost("curriculum")]
157+
public async Task<IActionResult> ProcessCvUpload(IFormFile profilePic, IFormFile curriculum) {
158+
var userId = GetUserId();
159+
if(!userId.HasValue) {
160+
return RedirectToAction(nameof(ShowCvUpload));
161+
}
162+
163+
if(profilePic != null) {
164+
ProcessProfilePicture(userId.Value, profilePic);
165+
}
166+
if(curriculum != null) {
167+
await ProcessCurriculum(userId.Value, curriculum);
168+
}
169+
170+
return RedirectToAction(nameof(ShowCvUpload));
114171
}
115172

116-
[HttpPost("cv-upload")]
117-
public IActionResult ProcessCvUpload() {
118-
return null;
173+
protected void ProcessProfilePicture(int userId, IFormFile file) {
174+
Logger.LogDebug("Processing profile picture, name {0} size {1}", file.FileName, file.Length);
175+
176+
var pathProfile = GetProfilePicPath(userId);
177+
using(var source = file.OpenReadStream()) {
178+
using(var img = Image.Load(source)) {
179+
Logger.LogTrace("Resizing input image");
180+
img.Mutate(x => x
181+
.AutoOrient()
182+
.Resize(new ResizeOptions {
183+
Mode = ResizeMode.Min,
184+
Size = new SixLabors.Primitives.Size(640, 640)
185+
})
186+
.Crop(640, 640)
187+
);
188+
189+
using(var output = new FileStream(pathProfile, FileMode.Create, FileAccess.Write)) {
190+
Logger.LogTrace("Writing file to {0}", pathProfile);
191+
img.SaveAsJpeg(output);
192+
}
193+
}
194+
}
195+
196+
Logger.LogInformation("Profile picture for user {0} uploaded to {1}", userId, pathProfile);
197+
}
198+
199+
protected async Task ProcessCurriculum(int userId, IFormFile file) {
200+
Logger.LogDebug("Processing curriculum, name {0} size {1}", file.FileName, file.Length);
201+
202+
var pathCurriculum = GetCurriculumPath(userId);
203+
using(var output = new FileStream(pathCurriculum, FileMode.Create, FileAccess.Write)) {
204+
Logger.LogTrace("Writing file to {0}", pathCurriculum);
205+
await file.CopyToAsync(output);
206+
}
207+
208+
Logger.LogInformation("Curriculum for user {0} uploaded to {1}", userId, pathCurriculum);
119209
}
120210

121211
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Threading.Tasks;
5+
6+
namespace CodeMooc.Web.Model {
7+
8+
public class DashboardUploadViewModel : DashboardBaseViewModel {
9+
10+
public string ProfilePictureFilename { get; set; }
11+
12+
public string CurriculumFilename { get; set; }
13+
14+
}
15+
16+
}

src/Web/Web/Startup.cs

+17
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.IO;
23
using CodeMooc.Web.Data;
34
using Microsoft.AspNetCore.Authentication.Cookies;
45
using Microsoft.AspNetCore.Authorization;
@@ -10,6 +11,7 @@
1011
using Microsoft.EntityFrameworkCore;
1112
using Microsoft.Extensions.Configuration;
1213
using Microsoft.Extensions.DependencyInjection;
14+
using Microsoft.Extensions.FileProviders;
1315

1416
namespace CodeMooc.Web {
1517

@@ -126,6 +128,21 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
126128

127129
app.UseStaticFiles();
128130

131+
// Add static file paths for uploaded files
132+
var pathsConf = Configuration.GetSection("Paths");
133+
134+
string pathCurricula = pathsConf["Curricula"];
135+
app.UseStaticFiles(new StaticFileOptions {
136+
FileProvider = new PhysicalFileProvider(Path.Combine("/data", pathCurricula)),
137+
RequestPath = "/uploads/curricula"
138+
});
139+
140+
string pathProfilePics = pathsConf["ProfilePics"];
141+
app.UseStaticFiles(new StaticFileOptions {
142+
FileProvider = new PhysicalFileProvider(Path.Combine("/data", pathProfilePics)),
143+
RequestPath = "/uploads/profiles"
144+
});
145+
129146
app.UseAuthentication();
130147

131148
app.UseMvc();

src/Web/Web/Views/Dashboard/Index.cshtml

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
<dt>@Html.ActionLink("Vedi stato socio", "ShowAssociationStatus")</dt>
2020
<dd>Verifica la tua registrazione come socio dell'associazione CodeMOOC.net ed i badge rilasciati.</dd>
2121

22-
<dt>@Html.ActionLink("Carica curriculum", "ShowCvUpload")</dt>
23-
<dd>Partecipa alla rete di competenze ed esperienze che l'associazione sta costruendo caricando il tuo curriculum.</dd>
22+
<dt>@Html.ActionLink("Raccolta curricula", "ShowCvUpload")</dt>
23+
<dd>Partecipa alla rete di competenze ed esperienze che l'associazione sta costruendo, caricando il tuo curriculum.</dd>
2424
</dl>
2525

2626
</div>
+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
@using CodeMooc.Web.Model
2+
@model DashboardUploadViewModel
3+
@{
4+
Layout = "Main";
5+
ViewData["Title"] = "Raccolta curricula";
6+
}
7+
8+
<div class="container">
9+
<h1 class="text-center">Raccolta curricula</h1>
10+
11+
<div class="col-12 col-sm-8 offset-sm-2 col-md-6 offset-md-3">
12+
13+
<p>◀&nbsp;@Html.ActionLink("Torna al pannello di controllo", "Index")</p>
14+
15+
<p>
16+
CodeMOOC.net sta sviluppando una <b>rete di competenze ed esperienze</b>, una via di mezzo tra una raccolta di curricula ed un social network.
17+
Questo strumento verrà <b>sviluppato su misura</b>, in modo che rappresenti al meglio la rete degli associati, le loro esperienze professionali e le relazioni che le valorizzano.
18+
</p>
19+
20+
<p>
21+
Tramite questo modulo raccoglieremo i curricula degli associati, in modo da usarli come base di dati reale attorno alla quale costruire lo strumento.
22+
</p>
23+
24+
<h2>Profilo utente</h2>
25+
26+
<p>
27+
Usa questo modulo per caricare un'immagine di profilo che ti rappresenti sulla rete di associati CodeMOOC.net.
28+
Utilizza immagini di formato quadrato.
29+
</p>
30+
31+
@if (Model.ProfilePictureFilename != null) {
32+
<p class="text-center">
33+
<img src="~/uploads/profiles/@Model.ProfilePictureFilename" alt="Profile utente" class="avatar img-fluid" />
34+
</p>
35+
}
36+
37+
<form asp-controller="Dashboard" asp-action="ProcessCvUpload" method="post" enctype="multipart/form-data" class="form-horizontal" role="form" asp-antiforgery="true">
38+
<div class="form-row">
39+
<div class="form-group">
40+
<label for="profilePic">Immagine profilo:</label>
41+
<input name="profilePic" type="file" class="input-form-control" />
42+
</div>
43+
44+
<button id="submit" type="submit" value="submit" class="btn btn-primary btn-block">Carica immagine profilo</button>
45+
</div>
46+
</form>
47+
48+
<h2>Curriculum</h2>
49+
50+
<p>
51+
Usa questo modulo per caricare il tuo curriculum in formato EuroPass: utilizza l'<a href="https://europass.cedefop.europa.eu/it/documents/curriculum-vitae">editor online</a> per creare il documento e trasmettilo qui in formato PDF.
52+
</p>
53+
54+
@if (Model.CurriculumFilename != null) {
55+
<p>
56+
<b>Il tuo curriculum è stato caricato correttamente!</b><br />
57+
File attuale: <a href="~/uploads/curricula/@Model.CurriculumFilename">@Model.CurriculumFilename</a>.
58+
</p>
59+
}
60+
61+
<form asp-controller="Dashboard" asp-action="ProcessCvUpload" method="post" enctype="multipart/form-data" class="form-horizontal" role="form" asp-antiforgery="true">
62+
<div class="form-row">
63+
<div class="form-group">
64+
<label for="curriculum">Curriculum:</label>
65+
<input name="curriculum" type="file" class="input-form-control" />
66+
</div>
67+
68+
<button id="submit" type="submit" value="submit" class="btn btn-primary btn-block">Carica curriculum</button>
69+
</div>
70+
</form>
71+
72+
</div>
73+
74+
</div>

src/Web/Web/Web.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
<PackageReference Include="MySql.Data" Version="8.0.17" />
1919
<PackageReference Include="MySql.Data.EntityFrameworkCore" Version="8.0.17" />
2020
<PackageReference Include="RestSharp" Version="106.6.10" />
21+
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.0-beta0006" />
2122
</ItemGroup>
2223

2324
<ItemGroup>

src/Web/Web/appsettings.json

+4
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,9 @@
1616
},
1717
"StaticUserPasswords": {
1818
"a.bogliolo": "$2y$12$jU9.9jJzc58TLq5Q1vYSTOuYR.4oLUL/vy006rCU69xP.wPeqqzN2"
19+
},
20+
"Paths": {
21+
"Curricula": "/data/curricula",
22+
"ProfilePics": "/data/profile"
1923
}
2024
}

src/Web/Web/wwwroot/signup.css

+12
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ button {
6565
color: rgb(172, 82, 138) !important;
6666
}
6767

68+
img.avatar {
69+
max-width: 50%;
70+
}
71+
6872
hr {
6973
margin-top: 2rem;
7074
margin-bottom: 2rem;
@@ -77,6 +81,14 @@ hr {
7781
margin-right: auto;
7882
}
7983

84+
.input-form-control {
85+
display: block;
86+
height: calc(2.25rem + 2px);
87+
font-size: 1rem;
88+
font-weight: 400;
89+
line-height: 1.5;
90+
}
91+
8092
#submit {
8193
background-color: rgb(202,112,168);
8294
border-color: rgb(202,112,168);

0 commit comments

Comments
 (0)