diff --git a/.gitignore b/.gitignore
index 0a8e0559ee40f980c98cf9af85ceef89e5cdc385..c524fb9e6a76f62d010c6ea470ea1966e71452a7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,4 @@
.vs
obj
bin
+StaticFiles
diff --git a/Tsi1.Api/Tsi1.Api/Controllers/CourseController.cs b/Tsi1.Api/Tsi1.Api/Controllers/CourseController.cs
index 07e1566697c0d69bfc9157b5d449be5cf4a9f575..7c82f7a8e4863b1150bebce631dcba9e8100412b 100644
--- a/Tsi1.Api/Tsi1.Api/Controllers/CourseController.cs
+++ b/Tsi1.Api/Tsi1.Api/Controllers/CourseController.cs
@@ -50,7 +50,6 @@ namespace Tsi1.Api.Controllers
public async Task<IActionResult> Create(CourseCreateDto newCourse)
{
var tenantId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "TenantId").Value);
-
newCourse.TenantId = tenantId;
var result = await _courseService.Create(newCourse);
@@ -59,10 +58,6 @@ namespace Tsi1.Api.Controllers
return BadRequest(result.Message);
}
- var path = Path.Combine(tenantId.ToString(), result.Data.Id.ToString());
-
- _fileService.CreateFolder(path);
-
return Ok();
}
@@ -214,10 +209,6 @@ namespace Tsi1.Api.Controllers
return NotFound(result.Message);
}
- var path = Path.Combine(result.Data.TenantId.ToString(), result.Data.Id.ToString());
-
- _fileService.DeleteFolder(path);
-
return Ok();
}
diff --git a/Tsi1.Api/Tsi1.Api/Controllers/FileController.cs b/Tsi1.Api/Tsi1.Api/Controllers/FileController.cs
index 144a8298f82892a2caafe88b4957da0c01b96b24..fb7296dfe4acc041f55513ebeb4dd8b42bc18489 100644
--- a/Tsi1.Api/Tsi1.Api/Controllers/FileController.cs
+++ b/Tsi1.Api/Tsi1.Api/Controllers/FileController.cs
@@ -22,13 +22,12 @@ namespace Tsi1.Api.Controllers
}
[Authorize(Roles = UserTypes.Professor)]
- [HttpPost("Create/{courseId}")]
- public async Task<IActionResult> Create(IFormFile file, int courseId)
+ [HttpPost("Upload/{courseId}")]
+ public async Task<IActionResult> Upload(IFormFile file, int courseId)
{
- var tenantId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "TenantId").Value);
-
- var result = await _fileService.Create(file, tenantId, courseId);
+ var tenantId = HttpContext.User.Claims.FirstOrDefault(x => x.Type == "TenantId").Value;
+ var result = await _fileService.Create(file, tenantId, courseId.ToString());
if (result.HasError)
{
return BadRequest(result.Message);
diff --git a/Tsi1.Api/Tsi1.Api/Controllers/ForumController.cs b/Tsi1.Api/Tsi1.Api/Controllers/ForumController.cs
index ce849fad5a8181e60e393cc4ddfc226540111847..83a96c4131d8d15ce2ab53da366c9b0c59477bef 100644
--- a/Tsi1.Api/Tsi1.Api/Controllers/ForumController.cs
+++ b/Tsi1.Api/Tsi1.Api/Controllers/ForumController.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@@ -23,10 +23,10 @@ namespace Tsi1.Api.Controllers
}
[Authorize(Roles = UserTypes.Student + ", " + UserTypes.Professor)]
- [HttpGet("GetForums/{courseId}")]
- public async Task<IActionResult> GetForums(int courseId)
+ [HttpGet("GetForums/{sectionId}")]
+ public async Task<IActionResult> GetForums(int sectionId)
{
- var result = await _forumService.GetForums(courseId);
+ var result = await _forumService.GetForums(sectionId);
if (result.HasError)
{
diff --git a/Tsi1.Api/Tsi1.Api/Controllers/SectionController.cs b/Tsi1.Api/Tsi1.Api/Controllers/SectionController.cs
new file mode 100644
index 0000000000000000000000000000000000000000..81946f2c304d9d9862e5a186937fee95d0ca7f32
--- /dev/null
+++ b/Tsi1.Api/Tsi1.Api/Controllers/SectionController.cs
@@ -0,0 +1,71 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Tsi1.BusinessLayer.Dtos;
+using Tsi1.BusinessLayer.Helpers;
+using Tsi1.BusinessLayer.Interfaces;
+
+namespace Tsi1.Api.Controllers
+{
+ [Authorize(Roles = UserTypes.Professor)]
+ [Route("api/[controller]")]
+ [ApiController]
+ public class SectionController : ControllerBase
+ {
+ private readonly ISectionService _sectionService;
+
+ public SectionController(ISectionService sectionService)
+ {
+ _sectionService = sectionService;
+ }
+
+ [HttpPost("Create")]
+ public async Task<IActionResult> Create(SectionCreateDto section)
+ {
+ var result = await _sectionService.Create(section);
+ if (result.HasError)
+ {
+ return BadRequest(result.Message);
+ }
+
+ return Ok();
+ }
+
+ [HttpPut("Modify/{sectionId}")]
+ public async Task<IActionResult> Modify(int sectionId, string newName)
+ {
+ var result = await _sectionService.Modify(sectionId, newName);
+ if (result.HasError)
+ {
+ return BadRequest(result.Message);
+ }
+
+ return Ok();
+ }
+
+ [HttpDelete("Delete/{sectionId}")]
+ public async Task<IActionResult> Delete(int sectionId)
+ {
+ var result = await _sectionService.Delete(sectionId);
+ if (result.HasError)
+ {
+ return NotFound(result.Message);
+ }
+
+ return Ok();
+ }
+
+ [HttpPost("ChangeOrder")]
+ public async Task<IActionResult> ChangeOrder(List<OrderDto> orderDtos)
+ {
+ var result = await _sectionService.OrderSections(orderDtos);
+ if (result.HasError)
+ {
+ return BadRequest(result.Message);
+ }
+
+ return Ok();
+ }
+ }
+}
diff --git a/Tsi1.Api/Tsi1.Api/Controllers/SectionItemController.cs b/Tsi1.Api/Tsi1.Api/Controllers/SectionItemController.cs
new file mode 100644
index 0000000000000000000000000000000000000000..4e278ca49c8c101e03e2c593a2cc5a895c3196d1
--- /dev/null
+++ b/Tsi1.Api/Tsi1.Api/Controllers/SectionItemController.cs
@@ -0,0 +1,68 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Tsi1.BusinessLayer.Dtos;
+using Tsi1.BusinessLayer.Helpers;
+using Tsi1.BusinessLayer.Interfaces;
+
+namespace Tsi1.Api.Controllers
+{
+ [Authorize(Roles = UserTypes.Professor)]
+ [Route("api/[controller]")]
+ [ApiController]
+ public class SectionItemController : ControllerBase
+ {
+ private readonly ISectionItemService _sectionItemService;
+ private readonly ISectionItemTypeService _sectionItemTypeService;
+
+ public SectionItemController(ISectionItemService sectionItemService, ISectionItemTypeService sectionItemTypeService)
+ {
+ _sectionItemService = sectionItemService;
+ _sectionItemTypeService = sectionItemTypeService;
+ }
+
+ [HttpPost("Create")]
+ public async Task<IActionResult> Create(SectionItemCreateDto sectionItem)
+ {
+ var result = await _sectionItemService.Create(sectionItem);
+ if (result.HasError)
+ {
+ return BadRequest(result.Message);
+ }
+
+ return Ok();
+ }
+
+ [HttpDelete("Delete/{sectionItemId}")]
+ public async Task<IActionResult> Delete(int sectionItemId)
+ {
+ var result = await _sectionItemService.Delete(sectionItemId);
+ if (result.HasError)
+ {
+ return NotFound(result.Message);
+ }
+
+ return Ok();
+ }
+
+ [HttpPost("ChangeOrder")]
+ public async Task<IActionResult> ChangeOrder(List<OrderDto> orderDtos)
+ {
+ var result = await _sectionItemService.OrderSectionItems(orderDtos);
+ if (result.HasError)
+ {
+ return BadRequest(result.Message);
+ }
+
+ return Ok();
+ }
+
+ [HttpGet("GetSectionItemTypes")]
+ public async Task<IActionResult> GetSectionItemTypes()
+ {
+ var result = await _sectionItemTypeService.GetAll();
+ return Ok(result.Data);
+ }
+ }
+}
diff --git a/Tsi1.Api/Tsi1.Api/Controllers/TenantController.cs b/Tsi1.Api/Tsi1.Api/Controllers/TenantController.cs
index 92a0665ffad2a0892a1365f1b16fbbf9730a92e6..9b597317ccc3eadd4638d7e5e46a72232ed7abc3 100644
--- a/Tsi1.Api/Tsi1.Api/Controllers/TenantController.cs
+++ b/Tsi1.Api/Tsi1.Api/Controllers/TenantController.cs
@@ -50,8 +50,6 @@ namespace Tsi1.Api.Controllers
return BadRequest(result.Message);
}
- _fileService.CreateFolder(result.Data.Id.ToString());
-
return Ok(result.Data);
}
@@ -90,8 +88,6 @@ namespace Tsi1.Api.Controllers
return NotFound(result.Message);
}
- _fileService.DeleteFolder(tenantId.ToString());
-
return Ok();
}
diff --git a/Tsi1.Api/Tsi1.Api/Startup.cs b/Tsi1.Api/Tsi1.Api/Startup.cs
index 991fdf3c7801ede272682689829eacea0793d2ed..6e94cdd7d44ba06c989ee6ce6c31f4ed44bca6b3 100644
--- a/Tsi1.Api/Tsi1.Api/Startup.cs
+++ b/Tsi1.Api/Tsi1.Api/Startup.cs
@@ -1,7 +1,5 @@
using System;
-using System.Collections.Generic;
using System.IO;
-using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AutoMapper;
@@ -9,8 +7,6 @@ using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.HttpsPolicy;
-using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
@@ -24,6 +20,7 @@ using Microsoft.OpenApi.Models;
using Tsi1.Api.Infrastructure;
using Tsi1.Api.SignalR;
using Tsi1.BusinessLayer.Helpers;
+using Tsi1.BusinessLayer.HostedServices;
using Tsi1.BusinessLayer.Interfaces;
using Tsi1.BusinessLayer.Services;
using Tsi1.DataLayer;
@@ -67,7 +64,7 @@ namespace Tsi1.Api
services.AddSingleton<IJwtAuthManager, JwtAuthManager>();
services.AddHostedService<JwtRefreshTokenCache>();
services.AddHostedService<JwtVerificationCodeCache>();
-
+ services.AddHostedService<FileCleaner>();
services.AddSingleton<IMessageService, MessageService>();
@@ -79,6 +76,9 @@ namespace Tsi1.Api
services.AddScoped<IPostMessageService, PostMessageService>();
services.AddScoped<ITenantService, TenantService>();
services.AddScoped<IFileService, FileService>();
+ services.AddScoped<ISectionService, SectionService>();
+ services.AddScoped<ISectionItemService, SectionItemService>();
+ services.AddScoped<ISectionItemTypeService, SectionItemTypeService>();
services.Configure<MailSettings>(Configuration.GetSection("MailSettings"));
services.AddScoped<IEmailService, EmailService>();
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Dtos/FileDto.cs b/Tsi1.Api/Tsi1.BusinessLayer/Dtos/FileDto.cs
index 977a0596daafe431f74776ab1dca5530a569655b..f16d17fcf63db34a88607f5e2a6b8626f8095bd3 100644
--- a/Tsi1.Api/Tsi1.BusinessLayer/Dtos/FileDto.cs
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Dtos/FileDto.cs
@@ -6,10 +6,7 @@ namespace Tsi1.BusinessLayer.Dtos
{
public class FileDto
{
- public int Id { get; set; }
-
public string Name { get; set; }
-
public string Path { get; set; }
}
}
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Dtos/ForumCreateDto.cs b/Tsi1.Api/Tsi1.BusinessLayer/Dtos/ForumCreateDto.cs
index b4ebbecf257f0cf9667d615450a776738d3bac0c..4c4d099f672bdb4d3babcfafa870142bfeb385e6 100644
--- a/Tsi1.Api/Tsi1.BusinessLayer/Dtos/ForumCreateDto.cs
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Dtos/ForumCreateDto.cs
@@ -8,7 +8,5 @@ namespace Tsi1.BusinessLayer.Dtos
public class ForumCreateDto
{
public string Name { get; set; }
-
- public int CourseId { get; set; }
}
}
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Dtos/OrderDto.cs b/Tsi1.Api/Tsi1.BusinessLayer/Dtos/OrderDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b5416344d88a50b8e42ea94771c4233319cad2e5
--- /dev/null
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Dtos/OrderDto.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tsi1.BusinessLayer.Dtos
+{
+ public class OrderDto
+ {
+ public int Id { get; set; }
+ public int Order { get; set; }
+ }
+}
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Dtos/SectionCreateDto.cs b/Tsi1.Api/Tsi1.BusinessLayer/Dtos/SectionCreateDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..3eee0e09604a69c76dd551964a95e139f1694f13
--- /dev/null
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Dtos/SectionCreateDto.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tsi1.BusinessLayer.Dtos
+{
+ public class SectionCreateDto
+ {
+ public int CourseId { get; set; }
+ public string Name { get; set; }
+ public int Order { get; set; }
+ }
+}
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Dtos/SectionItemCreateDto.cs b/Tsi1.Api/Tsi1.BusinessLayer/Dtos/SectionItemCreateDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..9da60f96c22ec4674298ff8503a8d29b0b70cfb8
--- /dev/null
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Dtos/SectionItemCreateDto.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tsi1.BusinessLayer.Dtos
+{
+ public class SectionItemCreateDto
+ {
+ public int Order { get; set; }
+ public int SectionId { get; set; }
+ public int SectionItemTypeId { get; set; }
+
+ public ForumCreateDto Forum { get; set; }
+ public FileDto File { get; set; }
+ }
+}
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Dtos/SectionItemTypeDto.cs b/Tsi1.Api/Tsi1.BusinessLayer/Dtos/SectionItemTypeDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..89a6ca311d3eca78d999f4b3c3448e2d21cc56b5
--- /dev/null
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Dtos/SectionItemTypeDto.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tsi1.BusinessLayer.Dtos
+{
+ public class SectionItemTypeDto
+ {
+ public int Id { get; set; }
+ public string Name { get; set; }
+ }
+}
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Helpers/ErrorMessages.cs b/Tsi1.Api/Tsi1.BusinessLayer/Helpers/ErrorMessages.cs
index 8dc7c9fb645af7ec4886187388f9ec2c5e662b8e..41b7cf04530200739506f9fc996dff91c0ac3b81 100644
--- a/Tsi1.Api/Tsi1.BusinessLayer/Helpers/ErrorMessages.cs
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Helpers/ErrorMessages.cs
@@ -42,5 +42,17 @@ namespace Tsi1.BusinessLayer.Helpers
public const string ErrorSavingFile = "No se pudo guardar el archivo '{0}'";
public const string ErrorDeletingFile = "No se pudo borrar el archivo '{0}'";
public const string DuplicateFile = "Ya existe el archivo '{0}'";
+ public const string FileDoesNotExist = "El archivo '{0}' no existe fisicamente en el file server";
+
+ public const string SectionDoesNotExist = "La seccion con id '{0}' no existe";
+ public const string DuplicateSectionOrder = "Hay secciones con el mismo orden";
+ public const string SectionItemDoesNotExist = "El item de seccion con id '{0}' no existe";
+ public const string DuplicateSectionItemOrder = "Hay items de seccion con el mismo orden";
+
+ public const string SectionItemTypeDoesNotExist = "El tipo de item de seccion con id '{0}' no existe";
+ public const string InvalidSectionItemType = "El tipo de item de seccion '{0}' es invalido";
+ public const string ForumIsNull = "El foro es nulo";
+ public const string FileIsNull = "El archivo es nulo";
+ public const string InvalidSectionItemData = "El tipo de item de seccion '{0}' no coincide con su contenido '{1}'";
}
}
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Helpers/MappingProfile.cs b/Tsi1.Api/Tsi1.BusinessLayer/Helpers/MappingProfile.cs
index 70331a3460dc0b13a3efd4f20f128faa0ac94a10..369144617f0eba6df858ecd73c83f401abac94fd 100644
--- a/Tsi1.Api/Tsi1.BusinessLayer/Helpers/MappingProfile.cs
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Helpers/MappingProfile.cs
@@ -33,6 +33,9 @@ namespace Tsi1.BusinessLayer.Helpers
CreateMap<Tenant, TenantCourseDto>().ForMember(x => x.Courses, opt => opt.Ignore());
CreateMap<UserType, UserTypeDto>();
CreateMap<File, FileDto>();
+ CreateMap<SectionItem, SectionItemCreateDto>();
+ CreateMap<Section, SectionCreateDto>();
+ CreateMap<SectionItemType, SectionItemTypeDto>();
CreateMap<ForumCreateDto, Forum>();
CreateMap<ForumPreviewDto, Forum>();
@@ -55,6 +58,9 @@ namespace Tsi1.BusinessLayer.Helpers
CreateMap<TenantCourseDto, Tenant>();
CreateMap<UserTypeDto, UserType>();
CreateMap<FileDto, File>();
+ CreateMap<SectionItemCreateDto, SectionItem>();
+ CreateMap<SectionCreateDto, Section>();
+ CreateMap<SectionItemTypeDto, SectionItemType>();
}
}
}
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Helpers/SectionItemTypes.cs b/Tsi1.Api/Tsi1.BusinessLayer/Helpers/SectionItemTypes.cs
new file mode 100644
index 0000000000000000000000000000000000000000..0eee4f0c3bd2dd3c53204e5539652b839534a794
--- /dev/null
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Helpers/SectionItemTypes.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tsi1.BusinessLayer.Helpers
+{
+ public static class SectionItemTypes
+ {
+ public const string Forum = nameof(Forum);
+ public const string File = nameof(File);
+ }
+}
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Helpers/ServiceResult.cs b/Tsi1.Api/Tsi1.BusinessLayer/Helpers/ServiceResult.cs
index ae294d7826fb64ea7a4037019e9003de5d84f9bc..2e9e2fe41e0fcc76bd20563837898a7e042511b8 100644
--- a/Tsi1.Api/Tsi1.BusinessLayer/Helpers/ServiceResult.cs
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Helpers/ServiceResult.cs
@@ -9,5 +9,17 @@ namespace Tsi1.BusinessLayer.Helpers
public T Data { get; set; }
public string Message { get; set; }
public bool HasError { get; set; }
+
+ public void AddMessage(string message)
+ {
+ if (string.IsNullOrEmpty(this.Message))
+ {
+ this.Message = message;
+ }
+ else
+ {
+ this.Message += System.Environment.NewLine + message;
+ }
+ }
}
}
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/HostedServices/FileCleaner.cs b/Tsi1.Api/Tsi1.BusinessLayer/HostedServices/FileCleaner.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7a5df1102adad587acc45fa417539d77993de5fa
--- /dev/null
+++ b/Tsi1.Api/Tsi1.BusinessLayer/HostedServices/FileCleaner.cs
@@ -0,0 +1,54 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using System;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Tsi1.BusinessLayer.Interfaces;
+
+namespace Tsi1.BusinessLayer.HostedServices
+{
+ public class FileCleaner : IHostedService, IDisposable
+ {
+ private Timer _timer;
+ private readonly IServiceScopeFactory _serviceScopeFactory;
+
+ public FileCleaner(IServiceScopeFactory serviceScopeFactory)
+ {
+ _serviceScopeFactory = serviceScopeFactory;
+ }
+
+ public Task StartAsync(CancellationToken cancellationToken)
+ {
+ _timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromDays(1));
+ return Task.CompletedTask;
+ }
+
+ private async void DoWork(object state)
+ {
+ using (var scope = _serviceScopeFactory.CreateScope())
+ {
+ var fileService = scope.ServiceProvider.GetRequiredService<IFileService>();
+ var dbFileNames = await fileService.GetFileNames();
+ var fsFiles = fileService.GetPhysicalFiles();
+
+ var filesToDelete = fsFiles.Except(dbFileNames);
+ foreach (var file in filesToDelete)
+ {
+ fileService.DeleteFile(file);
+ }
+ }
+ }
+
+ public Task StopAsync(CancellationToken cancellationToken)
+ {
+ _timer?.Change(Timeout.Infinite, 0);
+ return Task.CompletedTask;
+ }
+
+ public void Dispose()
+ {
+ _timer?.Dispose();
+ }
+ }
+}
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IFileService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IFileService.cs
index a42bbf693f410735a37715369574479ae29b6551..c0392df3d36d9d1f8f0e6af98659f92b49b1d438 100644
--- a/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IFileService.cs
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IFileService.cs
@@ -10,10 +10,10 @@ namespace Tsi1.BusinessLayer.Interfaces
{
public interface IFileService
{
- Task<ServiceResult<FileDto>> Create(IFormFile file, int tenantId, int courseId);
-
- void CreateFolder(string folderPath);
-
- void DeleteFolder(string folderPath);
+ Task<ServiceResult<string>> Create(IFormFile file, string tenantId, string courseId);
+ Task<List<string>> GetFileNames();
+ ServiceResult<bool> DeleteFile(string filePath);
+ bool ExistFile(string relativePath);
+ List<string> GetPhysicalFiles();
}
}
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IForumService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IForumService.cs
index 12d94600ab697452919232f3f12239614f811fe2..db95e50fb7187731b47a02cff78439a8ce5ee37a 100644
--- a/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IForumService.cs
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IForumService.cs
@@ -10,7 +10,7 @@ namespace Tsi1.BusinessLayer.Interfaces
{
public interface IForumService
{
- Task<ServiceResult<List<ForumPreviewDto>>> GetForums(int courseId);
+ Task<ServiceResult<List<ForumPreviewDto>>> GetForums(int sectionId);
Task<ServiceResult<Forum>> Create(ForumCreateDto newForum);
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/ISectionItemService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/ISectionItemService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..4834f03114b891bfae163d7d87ac1be83dfced28
--- /dev/null
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/ISectionItemService.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using Tsi1.BusinessLayer.Dtos;
+using Tsi1.BusinessLayer.Helpers;
+
+namespace Tsi1.BusinessLayer.Interfaces
+{
+ public interface ISectionItemService
+ {
+ Task<ServiceResult<bool>> Create(SectionItemCreateDto newSectionItem);
+ Task<ServiceResult<bool>> Delete(int sectionItemId);
+ Task<ServiceResult<bool>> OrderSectionItems(List<OrderDto> orderDtos);
+ }
+}
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/ISectionItemTypeService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/ISectionItemTypeService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..55112eb6f5da721e95ec5fba9da13b28b2e90fef
--- /dev/null
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/ISectionItemTypeService.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using Tsi1.BusinessLayer.Dtos;
+using Tsi1.BusinessLayer.Helpers;
+
+namespace Tsi1.BusinessLayer.Interfaces
+{
+ public interface ISectionItemTypeService
+ {
+ Task<ServiceResult<List<SectionItemTypeDto>>> GetAll();
+ Task<ServiceResult<SectionItemTypeDto>> GetById(int id);
+ }
+}
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/ISectionService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/ISectionService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..4ba20e791f0e16065ba0f14b1d4a5556ff6735ff
--- /dev/null
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/ISectionService.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using Tsi1.BusinessLayer.Dtos;
+using Tsi1.BusinessLayer.Helpers;
+
+namespace Tsi1.BusinessLayer.Interfaces
+{
+ public interface ISectionService
+ {
+ Task<ServiceResult<bool>> Create(SectionCreateDto newSection);
+ Task<ServiceResult<bool>> Delete(int sectionId);
+ Task<ServiceResult<bool>> OrderSections(List<OrderDto> orderDtos);
+ Task<ServiceResult<bool>> Modify(int sectionId, string name);
+ }
+}
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Services/FileService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Services/FileService.cs
index 315250148761311cbd020f1da7863798800a7793..74c8fb4ff7b641e327843a8acfdcb1cd6e3bd99b 100644
--- a/Tsi1.Api/Tsi1.BusinessLayer/Services/FileService.cs
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Services/FileService.cs
@@ -5,6 +5,7 @@ using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Tsi1.BusinessLayer.Dtos;
@@ -18,24 +19,28 @@ namespace Tsi1.BusinessLayer.Services
public class FileService : IFileService
{
private readonly Tsi1Context _context;
-
private readonly IMapper _mapper;
-
private readonly string _path;
public FileService(Tsi1Context context, IMapper mapper, IHostingEnvironment hostingEnvironment)
{
_context = context;
_mapper = mapper;
- _path = hostingEnvironment.ContentRootPath + "/StaticFiles";
+ _path = Path.Combine(hostingEnvironment.ContentRootPath, "StaticFiles");
}
- public async Task<ServiceResult<string>> SaveFile(IFormFile file, string filePath)
+ public async Task<ServiceResult<string>> Create(IFormFile file, string tenantId, string courseId)
{
var result = new ServiceResult<string>();
- var fileStream = new FileStream(filePath, FileMode.Create);
+ var folder = Path.Combine(_path, tenantId, courseId);
+ Directory.CreateDirectory(folder);
+ var fileName = GenerateFileName(file);
+ var relativePath = Path.Combine(tenantId, courseId, fileName);
+ var filePath = Path.Combine(_path, relativePath);
+
+ var fileStream = new FileStream(filePath, FileMode.Create);
try
{
await file.CopyToAsync(fileStream);
@@ -51,60 +56,11 @@ namespace Tsi1.BusinessLayer.Services
fileStream.Close();
}
+ result.Data = relativePath;
return result;
}
-
-
- public async Task<ServiceResult<FileDto>> Create(IFormFile file, int tenantId, int courseId)
- {
- var result = new ServiceResult<FileDto>();
-
- var tenant = await _context.Tenants
- .FirstOrDefaultAsync(x => x.Id == tenantId);
-
- var course = await _context.Courses
- .FirstOrDefaultAsync(x => x.Id == courseId);
-
- var filePath = Path.Combine(_path, tenant.Name, course.Name, file.FileName);
-
- var existingFile = await _context.Files.FirstOrDefaultAsync(x => x.Path == filePath);
- if (existingFile != null)
- {
- result.HasError = true;
- result.Message = string.Format(ErrorMessages.DuplicateFile, filePath);
- return result;
- }
-
- var newFile = new DataLayer.Entities.File
- {
- Name = file.FileName,
- Path = filePath.Replace(_path, ""),
- };
-
- _context.Add(newFile);
- await _context.SaveChangesAsync();
-
- var resultSaveFile = await this.SaveFile(file, filePath);
- if (resultSaveFile.HasError)
- {
- result.HasError = true;
- result.Message = resultSaveFile.Message;
-
- _context.Remove(newFile);
- await _context.SaveChangesAsync();
-
- return result;
- }
-
- var fileDto = _mapper.Map<FileDto>(newFile);
- result.Data = fileDto;
-
- return result;
- }
-
-
- public async Task<ServiceResult<bool>> DeleteFile(string filePath)
+ public ServiceResult<bool> DeleteFile(string filePath)
{
var result = new ServiceResult<bool>();
@@ -122,18 +78,29 @@ namespace Tsi1.BusinessLayer.Services
return result;
}
- public void CreateFolder(string folderPath)
+ public bool ExistFile(string relativePath)
{
- var path = Path.Combine(_path, folderPath);
+ var path = Path.Combine(_path, relativePath);
+ return System.IO.File.Exists(path);
+ }
- Directory.CreateDirectory(path);
+ public async Task<List<string>> GetFileNames()
+ {
+ return await _context.Files.Select(x => x.Path).ToListAsync();
}
- public void DeleteFolder(string folderPath)
+ public List<string> GetPhysicalFiles()
{
- var path = Path.Combine(_path, folderPath);
+ var fullPaths = Directory.GetFiles(_path, "*", SearchOption.AllDirectories);
+ return fullPaths.Select(x => Path.GetRelativePath(_path, x)).ToList();
+ }
- Directory.Delete(path);
+ private string GenerateFileName(IFormFile file)
+ {
+ var ext = file.FileName.Split(".").Last();
+ var fileName = Guid.NewGuid().ToString();
+ return fileName + "." + ext;
}
+
}
}
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Services/ForumService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Services/ForumService.cs
index 1865a424afa5cce020312e80eb3b5492318ed13f..6418cd586192711626386adcf25d8285a9bd63d0 100644
--- a/Tsi1.Api/Tsi1.BusinessLayer/Services/ForumService.cs
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Services/ForumService.cs
@@ -29,25 +29,12 @@ namespace Tsi1.BusinessLayer.Services
public async Task<ServiceResult<Forum>> Create(ForumCreateDto newForum)
{
var result = new ServiceResult<Forum>();
-
- var existingForum = await _context.Forums
- .FirstOrDefaultAsync(x => x.Name == newForum.Name && x.CourseId == newForum.CourseId);
-
- if (existingForum != null)
- {
- result.HasError = true;
- result.Message = string.Format(ErrorMessages.DuplicateForumName, newForum.Name);
- return result;
- }
-
var forum = _mapper.Map<Forum>(newForum);
_context.Forums.Add(forum);
-
await _context.SaveChangesAsync();
result.Data = forum;
-
return result;
}
@@ -71,13 +58,29 @@ namespace Tsi1.BusinessLayer.Services
return result;
}
- public async Task<ServiceResult<List<ForumPreviewDto>>> GetForums(int courseId)
+ public async Task<ServiceResult<List<ForumPreviewDto>>> GetForums(int sectionId)
{
var result = new ServiceResult<List<ForumPreviewDto>>();
- var forums = await _context.Forums
- .Where(x => x.CourseId == courseId)
- .ToListAsync();
+ var section = await _context.Sections
+ .Where(x => x.Id == sectionId)
+ .Include(x => x.SectionItems)
+ .ThenInclude(x => x.SectionItemType)
+ .Include(x => x.SectionItems)
+ .ThenInclude(x => x.Forum)
+ .FirstOrDefaultAsync();
+
+ if (section == null)
+ {
+ result.HasError = true;
+ result.Message = string.Format(ErrorMessages.SectionDoesNotExist, sectionId);
+ return result;
+ }
+
+ var forums = section.SectionItems
+ .Where(x => x.SectionItemType.Name == SectionItemTypes.Forum)
+ .Select(x => x.Forum)
+ .ToList();
var forumDtos = _mapper.Map<List<ForumPreviewDto>>(forums);
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Services/SectionItemService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Services/SectionItemService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..ae0eedca11130f202ca5cc31688ea5725eea6525
--- /dev/null
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Services/SectionItemService.cs
@@ -0,0 +1,156 @@
+using AutoMapper;
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tsi1.BusinessLayer.Dtos;
+using Tsi1.BusinessLayer.Helpers;
+using Tsi1.BusinessLayer.Interfaces;
+using Tsi1.DataLayer;
+using Tsi1.DataLayer.Entities;
+
+namespace Tsi1.BusinessLayer.Services
+{
+ public class SectionItemService : ISectionItemService
+ {
+ private readonly IMapper _mapper;
+ private readonly Tsi1Context _context;
+ private readonly IFileService _fileService;
+
+ public SectionItemService(IMapper mapper, Tsi1Context context, IFileService fileService)
+ {
+ _mapper = mapper;
+ _context = context;
+ _fileService = fileService;
+ }
+
+ public async Task<ServiceResult<bool>> Create(SectionItemCreateDto newSectionItem)
+ {
+ var sectionItem = _mapper.Map<SectionItem>(newSectionItem);
+
+ var result = await this.SectionItemValidations(sectionItem);
+
+ if (result.HasError)
+ {
+ return result;
+ }
+
+ _context.SectionItems.Add(sectionItem);
+ await _context.SaveChangesAsync();
+
+ return result;
+ }
+
+ public async Task<ServiceResult<bool>> Delete(int sectionItemId)
+ {
+ var result = new ServiceResult<bool>();
+
+ var sectionItem = await _context.SectionItems.FirstOrDefaultAsync(x => x.Id == sectionItemId);
+ if (sectionItem == null)
+ {
+ result.HasError = true;
+ result.AddMessage(string.Format(ErrorMessages.SectionItemDoesNotExist, sectionItemId));
+ return result;
+ }
+
+ _context.SectionItems.Remove(sectionItem);
+ await _context.SaveChangesAsync();
+
+ return result;
+ }
+
+ public async Task<ServiceResult<bool>> OrderSectionItems(List<OrderDto> orderDtos)
+ {
+ var result = new ServiceResult<bool>();
+
+ var sectionItemIds = orderDtos.Select(x => x.Id);
+ var orders = orderDtos.Select(x => x.Order).Distinct();
+
+ if (orders.Count() != orderDtos.Count())
+ {
+ result.HasError = true;
+ result.AddMessage(ErrorMessages.DuplicateSectionItemOrder);
+ return result;
+ }
+
+ var sectionItems = await _context.SectionItems
+ .Where(x => sectionItemIds.Contains(x.Id))
+ .ToListAsync();
+
+ foreach (var orderDto in orderDtos)
+ {
+ var sectionItem = sectionItems.FirstOrDefault(x => x.Id == orderDto.Id);
+ if (sectionItem == null)
+ {
+ result.HasError = true;
+ result.AddMessage(string.Format(ErrorMessages.SectionItemDoesNotExist, orderDto.Id));
+ }
+
+ sectionItem.Order = orderDto.Order;
+ }
+
+ if (!result.HasError)
+ {
+ await _context.SaveChangesAsync();
+ }
+
+ return result;
+ }
+
+ private async Task<ServiceResult<bool>> SectionItemValidations(SectionItem sectionItem)
+ {
+ var result = new ServiceResult<bool>();
+
+ var sectionItemType = await _context.SectionItemTypes
+ .FirstOrDefaultAsync(x => x.Id == sectionItem.SectionItemTypeId);
+
+ if (sectionItemType == null)
+ {
+ result.HasError = true;
+ result.AddMessage(string.Format(ErrorMessages.InvalidSectionItemType, sectionItem.SectionItemTypeId));
+ return result;
+ }
+
+ if (sectionItemType.Name == SectionItemTypes.Forum)
+ {
+ if (sectionItem.Forum == null)
+ {
+ result.HasError = true;
+ result.AddMessage(ErrorMessages.ForumIsNull);
+ }
+
+ if (sectionItem.File != null)
+ {
+ result.HasError = true;
+ result.AddMessage(string.Format(ErrorMessages.InvalidSectionItemData, sectionItemType.Name, SectionItemTypes.File));
+ }
+ }
+
+ if (sectionItemType.Name == SectionItemTypes.File)
+ {
+ if (sectionItem.File == null)
+ {
+ result.HasError = true;
+ result.AddMessage(ErrorMessages.FileIsNull);
+ }
+
+ if (sectionItem.Forum != null)
+ {
+ result.HasError = true;
+ result.AddMessage(string.Format(ErrorMessages.InvalidSectionItemData, sectionItemType.Name, SectionItemTypes.Forum));
+ }
+
+ if (!_fileService.ExistFile(sectionItem.File.Path))
+ {
+ result.HasError = true;
+ result.AddMessage(string.Format(ErrorMessages.FileDoesNotExist, sectionItem.File.Path));
+ }
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Services/SectionItemTypeService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Services/SectionItemTypeService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..28b4a6b24f979d8e5cd5ee51a46dd62c7d011091
--- /dev/null
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Services/SectionItemTypeService.cs
@@ -0,0 +1,49 @@
+using AutoMapper;
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using Tsi1.BusinessLayer.Dtos;
+using Tsi1.BusinessLayer.Helpers;
+using Tsi1.BusinessLayer.Interfaces;
+using Tsi1.DataLayer;
+
+namespace Tsi1.BusinessLayer.Services
+{
+ public class SectionItemTypeService : ISectionItemTypeService
+ {
+ private readonly Tsi1Context _context;
+ private readonly IMapper _mapper;
+
+ public SectionItemTypeService(Tsi1Context context, IMapper mapper)
+ {
+ _context = context;
+ _mapper = mapper;
+ }
+ public async Task<ServiceResult<List<SectionItemTypeDto>>> GetAll()
+ {
+ var result = new ServiceResult<List<SectionItemTypeDto>>();
+ var sectionItemTypes = await _context.SectionItemTypes.ToListAsync();
+
+ result.Data = _mapper.Map<List<SectionItemTypeDto>>(sectionItemTypes);
+ return result;
+ }
+
+ public async Task<ServiceResult<SectionItemTypeDto>> GetById(int id)
+ {
+ var result = new ServiceResult<SectionItemTypeDto>();
+
+ var sectionItemType = await _context.SectionItemTypes.FirstOrDefaultAsync(x => x.Id == id);
+ if (sectionItemType == null)
+ {
+ result.HasError = true;
+ result.AddMessage(string.Format(ErrorMessages.SectionItemTypeDoesNotExist, id));
+ return result;
+ }
+
+ result.Data = _mapper.Map<SectionItemTypeDto>(sectionItemType);
+ return result;
+ }
+ }
+}
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Services/SectionService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Services/SectionService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..9da29bb8ee10dd2800e7977d86582cc564b7917b
--- /dev/null
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Services/SectionService.cs
@@ -0,0 +1,118 @@
+using AutoMapper;
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tsi1.BusinessLayer.Dtos;
+using Tsi1.BusinessLayer.Helpers;
+using Tsi1.BusinessLayer.Interfaces;
+using Tsi1.DataLayer;
+using Tsi1.DataLayer.Entities;
+
+namespace Tsi1.BusinessLayer.Services
+{
+ public class SectionService : ISectionService
+ {
+ private readonly Tsi1Context _context;
+ private readonly IMapper _mapper;
+
+ public SectionService(Tsi1Context context, IMapper mapper)
+ {
+ _context = context;
+ _mapper = mapper;
+ }
+
+ public async Task<ServiceResult<bool>> Create(SectionCreateDto newSection)
+ {
+ var result = new ServiceResult<bool>();
+
+ var course = await _context.Courses.FirstOrDefaultAsync(x => x.Id == newSection.CourseId);
+ if (course == null)
+ {
+ result.HasError = true;
+ result.AddMessage(string.Format(ErrorMessages.CourseDoesNotExist, newSection.CourseId));
+ return result;
+ }
+
+ var section = _mapper.Map<Section>(newSection);
+ _context.Sections.Add(section);
+ await _context.SaveChangesAsync();
+
+ return result;
+ }
+
+ public async Task<ServiceResult<bool>> Delete(int sectionId)
+ {
+ var result = new ServiceResult<bool>();
+
+ var section = await _context.Sections.FirstOrDefaultAsync(x => x.Id == sectionId);
+ if (section == null)
+ {
+ result.HasError = true;
+ result.AddMessage(string.Format(ErrorMessages.SectionDoesNotExist, sectionId));
+ return result;
+ }
+
+ _context.Sections.Remove(section);
+ await _context.SaveChangesAsync();
+ return result;
+ }
+
+ public async Task<ServiceResult<bool>> Modify(int sectionId, string name)
+ {
+ var result = new ServiceResult<bool>();
+
+ var section = await _context.Sections.FirstOrDefaultAsync(x => x.Id == sectionId);
+ if (section == null)
+ {
+ result.HasError = true;
+ result.AddMessage(string.Format(ErrorMessages.SectionDoesNotExist, sectionId));
+ return result;
+ }
+
+ section.Name = name;
+ await _context.SaveChangesAsync();
+ return result;
+ }
+
+ public async Task<ServiceResult<bool>> OrderSections(List<OrderDto> orderDtos)
+ {
+ var result = new ServiceResult<bool>();
+
+ var sectionIds = orderDtos.Select(x => x.Id);
+ var orders = orderDtos.Select(x => x.Order).Distinct();
+
+ if (orders.Count() != orderDtos.Count())
+ {
+ result.HasError = true;
+ result.AddMessage(ErrorMessages.DuplicateSectionOrder);
+ return result;
+ }
+
+ var sections = await _context.Sections
+ .Where(x => sectionIds.Contains(x.Id))
+ .ToListAsync();
+
+ foreach (var orderDto in orderDtos)
+ {
+ var section = sections.FirstOrDefault(x => x.Id == orderDto.Id);
+ if (section == null)
+ {
+ result.HasError = true;
+ result.AddMessage(string.Format(ErrorMessages.SectionDoesNotExist, orderDto.Id));
+ }
+
+ section.Order = orderDto.Order;
+ }
+
+ if (!result.HasError)
+ {
+ await _context.SaveChangesAsync();
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/Entities/Course.cs b/Tsi1.Api/Tsi1.DataLayer/Entities/Course.cs
index 0f4f050705ec5081ef0cd43de7f76b59f259d269..942858fad3a54d8c23bb6142258eb9bdf7e14b23 100644
--- a/Tsi1.Api/Tsi1.DataLayer/Entities/Course.cs
+++ b/Tsi1.Api/Tsi1.DataLayer/Entities/Course.cs
@@ -10,19 +10,16 @@ namespace Tsi1.DataLayer.Entities
{
StudentCourses = new HashSet<StudentCourse>();
ProfessorCourses = new HashSet<ProfessorCourse>();
- Forums = new HashSet<Forum>();
+ Sections = new HashSet<Section>();
}
public int Id { get; set; }
-
public string Name { get; set; }
-
public int TenantId { get; set; }
public Tenant Tenant { get; set; }
-
public ICollection<StudentCourse> StudentCourses { get; set; }
public ICollection<ProfessorCourse> ProfessorCourses { get; set; }
- public ICollection<Forum> Forums { get; set; }
+ public ICollection<Section> Sections { get; set; }
}
}
diff --git a/Tsi1.Api/Tsi1.DataLayer/Entities/File.cs b/Tsi1.Api/Tsi1.DataLayer/Entities/File.cs
index 4643d9c27c0f79917b66a41b0bff4435b6cfdc21..ea3324a77bd99ec20579f7aee468186c3c4bd643 100644
--- a/Tsi1.Api/Tsi1.DataLayer/Entities/File.cs
+++ b/Tsi1.Api/Tsi1.DataLayer/Entities/File.cs
@@ -7,9 +7,9 @@ namespace Tsi1.DataLayer.Entities
public class File
{
public int Id { get; set; }
-
public string Name { get; set; }
-
public string Path { get; set; }
+
+ public SectionItem SectionItem { get; set; }
}
}
diff --git a/Tsi1.Api/Tsi1.DataLayer/Entities/Forum.cs b/Tsi1.Api/Tsi1.DataLayer/Entities/Forum.cs
index 197e1e01af9d0576764a418b53a2e165a0834ada..086df15e9e457f34c6715fb4a79a99b44da5bcb1 100644
--- a/Tsi1.Api/Tsi1.DataLayer/Entities/Forum.cs
+++ b/Tsi1.Api/Tsi1.DataLayer/Entities/Forum.cs
@@ -13,13 +13,9 @@ namespace Tsi1.DataLayer.Entities
}
public int Id { get; set; }
-
public string Name { get; set; }
- public int CourseId { get; set; }
-
- public Course Course { get; set; }
-
+ public SectionItem SectionItem { get; set; }
public ICollection<Post> Posts { get; set; }
public ICollection<ForumUser> ForumUsers { get; set; }
}
diff --git a/Tsi1.Api/Tsi1.DataLayer/Entities/Section.cs b/Tsi1.Api/Tsi1.DataLayer/Entities/Section.cs
new file mode 100644
index 0000000000000000000000000000000000000000..db0f9d9a724bcadbf044e92cc5742585eb4208e9
--- /dev/null
+++ b/Tsi1.Api/Tsi1.DataLayer/Entities/Section.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tsi1.DataLayer.Entities
+{
+ public class Section
+ {
+ public Section()
+ {
+ SectionItems = new HashSet<SectionItem>();
+ }
+
+ public int Id { get; set; }
+ public int CourseId { get; set; }
+ public string Name { get; set; }
+ public int Order { get; set; }
+
+ public Course Course { get; set; }
+ public ICollection<SectionItem> SectionItems { get; set; }
+ }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/Entities/SectionItem.cs b/Tsi1.Api/Tsi1.DataLayer/Entities/SectionItem.cs
new file mode 100644
index 0000000000000000000000000000000000000000..3bd1ea22fc82fb58c6cc1015adeeae598bc64c67
--- /dev/null
+++ b/Tsi1.Api/Tsi1.DataLayer/Entities/SectionItem.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tsi1.DataLayer.Entities
+{
+ public class SectionItem
+ {
+ public int Id { get; set; }
+ public int Order { get; set; }
+ public int SectionId { get; set; }
+ public int SectionItemTypeId { get; set; }
+ public int? ForumId { get; set; }
+ public int? FileId { get; set; }
+
+ public Section Section { get; set; }
+ public SectionItemType SectionItemType { get; set; }
+ public Forum Forum { get; set; }
+ public File File { get; set; }
+ }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/Entities/SectionItemType.cs b/Tsi1.Api/Tsi1.DataLayer/Entities/SectionItemType.cs
new file mode 100644
index 0000000000000000000000000000000000000000..438d864848fefdcab64360ffd4790474f66f1eb5
--- /dev/null
+++ b/Tsi1.Api/Tsi1.DataLayer/Entities/SectionItemType.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tsi1.DataLayer.Entities
+{
+ public class SectionItemType
+ {
+ public SectionItemType()
+ {
+ SectionItems = new HashSet<SectionItem>();
+ }
+
+ public int Id { get; set; }
+ public string Name { get; set; }
+
+ public ICollection<SectionItem> SectionItems { get; set; }
+ }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/ForumConfiguration.cs b/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/ForumConfiguration.cs
index ac2d37069880b40303ee5208d896a1b3e1ed968e..777e05129d429060775725fc5e81920c96ddf2e6 100644
--- a/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/ForumConfiguration.cs
+++ b/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/ForumConfiguration.cs
@@ -1,30 +1,18 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
-using System;
-using System.Collections.Generic;
-using System.Security.Cryptography.X509Certificates;
-using System.Text;
using Tsi1.DataLayer.Entities;
namespace Tsi1.DataLayer.EntityConfiguration
{
- class ForumConfiguration : IEntityTypeConfiguration<Forum>
+ public class ForumConfiguration : IEntityTypeConfiguration<Forum>
{
public void Configure(EntityTypeBuilder<Forum> builder)
{
builder.HasKey(x => x.Id);
- builder.HasIndex(x => new { x.CourseId, x.Name})
- .IsUnique();
-
builder.Property(x => x.Name)
.IsRequired()
.HasColumnType("character varying(50)");
-
- builder.HasOne(x => x.Course)
- .WithMany(x => x.Forums)
- .HasForeignKey(x => x.CourseId);
-
}
}
}
diff --git a/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/SectionConfiguration.cs b/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/SectionConfiguration.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b87104c8039f37e3e020ddf226dc1bc73e75177c
--- /dev/null
+++ b/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/SectionConfiguration.cs
@@ -0,0 +1,28 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Tsi1.DataLayer.Entities;
+
+namespace Tsi1.DataLayer.EntityConfiguration
+{
+ public class SectionConfiguration : IEntityTypeConfiguration<Section>
+ {
+ public void Configure(EntityTypeBuilder<Section> builder)
+ {
+ builder.HasKey(x => x.Id);
+
+ builder.Property(x => x.Name)
+ .IsRequired()
+ .HasColumnType("character varying(255)");
+
+ builder.Property(x => x.Order)
+ .IsRequired();
+
+ builder.HasOne(x => x.Course)
+ .WithMany(x => x.Sections)
+ .HasForeignKey(x => x.CourseId);
+ }
+ }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/SectionItemConfiguration.cs b/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/SectionItemConfiguration.cs
new file mode 100644
index 0000000000000000000000000000000000000000..ffcda8dfd6c0f26718219dd2e30e04fcd6a1db81
--- /dev/null
+++ b/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/SectionItemConfiguration.cs
@@ -0,0 +1,36 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Tsi1.DataLayer.Entities;
+
+namespace Tsi1.DataLayer.EntityConfiguration
+{
+ public class SectionItemConfiguration : IEntityTypeConfiguration<SectionItem>
+ {
+ public void Configure(EntityTypeBuilder<SectionItem> builder)
+ {
+ builder.HasKey(x => x.Id);
+
+ builder.Property(x => x.Order)
+ .IsRequired();
+
+ builder.HasOne(x => x.File)
+ .WithOne(x => x.SectionItem)
+ .HasForeignKey<SectionItem>(x => x.FileId);
+
+ builder.HasOne(x => x.Forum)
+ .WithOne(x => x.SectionItem)
+ .HasForeignKey<SectionItem>(x => x.ForumId);
+
+ builder.HasOne(x => x.SectionItemType)
+ .WithMany(x => x.SectionItems)
+ .HasForeignKey(x => x.SectionItemTypeId);
+
+ builder.HasOne(x => x.Section)
+ .WithMany(x => x.SectionItems)
+ .HasForeignKey(x => x.SectionId);
+ }
+ }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/SectionItemTypeConfiguration.cs b/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/SectionItemTypeConfiguration.cs
new file mode 100644
index 0000000000000000000000000000000000000000..fdef28196b29b67daa5bea60219ad4370d7067f9
--- /dev/null
+++ b/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/SectionItemTypeConfiguration.cs
@@ -0,0 +1,21 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Tsi1.DataLayer.Entities;
+
+namespace Tsi1.DataLayer.EntityConfiguration
+{
+ public class SectionItemTypeConfiguration : IEntityTypeConfiguration<SectionItemType>
+ {
+ public void Configure(EntityTypeBuilder<SectionItemType> builder)
+ {
+ builder.HasKey(x => x.Id);
+
+ builder.Property(x => x.Name)
+ .IsRequired()
+ .HasColumnType("character varying(100)");
+ }
+ }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/Migrations/20201106232002_section_items.Designer.cs b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201106232002_section_items.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..bffe3f02174a06798dd1fb577ae58969db7a02e8
--- /dev/null
+++ b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201106232002_section_items.Designer.cs
@@ -0,0 +1,612 @@
+// <auto-generated />
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+using Tsi1.DataLayer;
+
+namespace Tsi1.DataLayer.Migrations
+{
+ [DbContext(typeof(Tsi1Context))]
+ [Migration("20201106232002_section_items")]
+ partial class section_items
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
+ .HasAnnotation("ProductVersion", "3.1.4")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Connection", b =>
+ {
+ b.Property<string>("ConnectionId")
+ .HasColumnType("text");
+
+ b.Property<string>("GroupName")
+ .HasColumnType("text");
+
+ b.Property<int>("UserId")
+ .HasColumnType("integer");
+
+ b.HasKey("ConnectionId");
+
+ b.HasIndex("GroupName");
+
+ b.ToTable("Connections");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Course", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("character varying(50)");
+
+ b.Property<int>("TenantId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("TenantId");
+
+ b.HasIndex("Name", "TenantId")
+ .IsUnique();
+
+ b.ToTable("Courses");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.File", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("character varying(50)");
+
+ b.Property<string>("Path")
+ .IsRequired()
+ .HasColumnType("character varying(1000)");
+
+ b.Property<int>("SectionItemId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Path")
+ .IsUnique();
+
+ b.ToTable("Files");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Forum", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<int?>("CourseId")
+ .HasColumnType("integer");
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("character varying(50)");
+
+ b.Property<int>("SectionItemId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CourseId");
+
+ b.ToTable("Forums");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.ForumUser", b =>
+ {
+ b.Property<int>("ForumId")
+ .HasColumnType("integer");
+
+ b.Property<int>("UserId")
+ .HasColumnType("integer");
+
+ b.HasKey("ForumId", "UserId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("ForumUsers");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Group", b =>
+ {
+ b.Property<string>("Name")
+ .HasColumnType("text");
+
+ b.HasKey("Name");
+
+ b.ToTable("Groups");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Post", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<DateTime>("Date")
+ .HasColumnType("timestamp without time zone");
+
+ b.Property<int>("ForumId")
+ .HasColumnType("integer");
+
+ b.Property<string>("Title")
+ .IsRequired()
+ .HasColumnType("character varying(100)");
+
+ b.Property<int>("UserId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.HasIndex("ForumId", "Title")
+ .IsUnique();
+
+ b.ToTable("Posts");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.PostMessage", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("Content")
+ .IsRequired()
+ .HasColumnType("character varying(10485760)");
+
+ b.Property<DateTime>("Date")
+ .HasColumnType("timestamp without time zone");
+
+ b.Property<int>("PostId")
+ .HasColumnType("integer");
+
+ b.Property<int>("UserId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("PostId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("PostMessages");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Professor", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("IdentityCard")
+ .IsRequired()
+ .HasColumnType("character varying(50)");
+
+ b.Property<int>("TenantId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("TenantId");
+
+ b.HasIndex("IdentityCard", "TenantId")
+ .IsUnique();
+
+ b.ToTable("Professors");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.ProfessorCourse", b =>
+ {
+ b.Property<int>("ProfessorId")
+ .HasColumnType("integer");
+
+ b.Property<int>("CourseId")
+ .HasColumnType("integer");
+
+ b.HasKey("ProfessorId", "CourseId");
+
+ b.HasIndex("CourseId");
+
+ b.ToTable("ProfessorCourses");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Section", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("character varying(255)");
+
+ b.Property<int>("Order")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.ToTable("Sections");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.SectionItem", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<int?>("FileId")
+ .HasColumnType("integer");
+
+ b.Property<int?>("ForumId")
+ .HasColumnType("integer");
+
+ b.Property<int>("Order")
+ .HasColumnType("integer");
+
+ b.Property<int>("SectionId")
+ .HasColumnType("integer");
+
+ b.Property<int>("SectionItemTypeId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("FileId")
+ .IsUnique();
+
+ b.HasIndex("ForumId")
+ .IsUnique();
+
+ b.HasIndex("SectionId");
+
+ b.HasIndex("SectionItemTypeId");
+
+ b.ToTable("SectionItems");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.SectionItemType", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("character varying(100)");
+
+ b.HasKey("Id");
+
+ b.ToTable("SectionItemTypes");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Student", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<int>("Age")
+ .HasColumnType("integer");
+
+ b.Property<string>("IdentityCard")
+ .IsRequired()
+ .HasColumnType("character varying(50)");
+
+ b.Property<int>("TenantId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("TenantId");
+
+ b.HasIndex("IdentityCard", "TenantId")
+ .IsUnique();
+
+ b.ToTable("Students");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.StudentCourse", b =>
+ {
+ b.Property<int>("StudentId")
+ .HasColumnType("integer");
+
+ b.Property<int>("CourseId")
+ .HasColumnType("integer");
+
+ b.HasKey("StudentId", "CourseId");
+
+ b.HasIndex("CourseId");
+
+ b.ToTable("StudentCourses");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Tenant", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("character varying(50)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.ToTable("Tenants");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.User", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("Email")
+ .IsRequired()
+ .HasColumnType("character varying(255)");
+
+ b.Property<string>("FirstName")
+ .IsRequired()
+ .HasColumnType("character varying(255)");
+
+ b.Property<string>("LastName")
+ .IsRequired()
+ .HasColumnType("character varying(255)");
+
+ b.Property<string>("Password")
+ .IsRequired()
+ .HasColumnType("character varying(255)");
+
+ b.Property<int?>("ProfessorId")
+ .HasColumnType("integer");
+
+ b.Property<int?>("StudentId")
+ .HasColumnType("integer");
+
+ b.Property<int>("TenantId")
+ .HasColumnType("integer");
+
+ b.Property<int>("UserTypeId")
+ .HasColumnType("integer");
+
+ b.Property<string>("Username")
+ .IsRequired()
+ .HasColumnType("character varying(50)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ProfessorId")
+ .IsUnique();
+
+ b.HasIndex("StudentId")
+ .IsUnique();
+
+ b.HasIndex("TenantId");
+
+ b.HasIndex("UserTypeId");
+
+ b.HasIndex("Username", "TenantId")
+ .IsUnique();
+
+ b.ToTable("Users");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.UserType", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("character varying(50)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.ToTable("UserTypes");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Connection", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Group", "Group")
+ .WithMany("Connections")
+ .HasForeignKey("GroupName");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Course", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
+ .WithMany("Courses")
+ .HasForeignKey("TenantId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Forum", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Course", null)
+ .WithMany("Forums")
+ .HasForeignKey("CourseId");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.ForumUser", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Forum", "Forum")
+ .WithMany("ForumUsers")
+ .HasForeignKey("ForumId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tsi1.DataLayer.Entities.User", "User")
+ .WithMany("ForumUsers")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Post", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Forum", "Forum")
+ .WithMany("Posts")
+ .HasForeignKey("ForumId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tsi1.DataLayer.Entities.User", "User")
+ .WithMany("Posts")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.PostMessage", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Post", "Post")
+ .WithMany("PostMessages")
+ .HasForeignKey("PostId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tsi1.DataLayer.Entities.User", "User")
+ .WithMany("PostMessages")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Professor", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
+ .WithMany("Professors")
+ .HasForeignKey("TenantId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.ProfessorCourse", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Course", "Course")
+ .WithMany("ProfessorCourses")
+ .HasForeignKey("CourseId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tsi1.DataLayer.Entities.Professor", "Professor")
+ .WithMany("ProfessorCourses")
+ .HasForeignKey("ProfessorId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.SectionItem", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.File", "File")
+ .WithOne("SectionItem")
+ .HasForeignKey("Tsi1.DataLayer.Entities.SectionItem", "FileId");
+
+ b.HasOne("Tsi1.DataLayer.Entities.Forum", "Forum")
+ .WithOne("SectionItem")
+ .HasForeignKey("Tsi1.DataLayer.Entities.SectionItem", "ForumId");
+
+ b.HasOne("Tsi1.DataLayer.Entities.Section", "Section")
+ .WithMany("SectionItems")
+ .HasForeignKey("SectionId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tsi1.DataLayer.Entities.SectionItemType", "SectionItemType")
+ .WithMany("SectionItems")
+ .HasForeignKey("SectionItemTypeId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Student", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
+ .WithMany("Students")
+ .HasForeignKey("TenantId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.StudentCourse", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Course", "Course")
+ .WithMany("StudentCourses")
+ .HasForeignKey("CourseId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tsi1.DataLayer.Entities.Student", "Student")
+ .WithMany("StudentCourses")
+ .HasForeignKey("StudentId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.User", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Professor", "Professor")
+ .WithOne("User")
+ .HasForeignKey("Tsi1.DataLayer.Entities.User", "ProfessorId");
+
+ b.HasOne("Tsi1.DataLayer.Entities.Student", "Student")
+ .WithOne("User")
+ .HasForeignKey("Tsi1.DataLayer.Entities.User", "StudentId");
+
+ b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
+ .WithMany("Users")
+ .HasForeignKey("TenantId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tsi1.DataLayer.Entities.UserType", "UserType")
+ .WithMany()
+ .HasForeignKey("UserTypeId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/Migrations/20201106232002_section_items.cs b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201106232002_section_items.cs
new file mode 100644
index 0000000000000000000000000000000000000000..c3b4c6e50eddb56dc2e4f127073d40e3123cf87d
--- /dev/null
+++ b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201106232002_section_items.cs
@@ -0,0 +1,191 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+namespace Tsi1.DataLayer.Migrations
+{
+ public partial class section_items : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropForeignKey(
+ name: "FK_Forums_Courses_CourseId",
+ table: "Forums");
+
+ migrationBuilder.DropIndex(
+ name: "IX_Forums_CourseId_Name",
+ table: "Forums");
+
+ migrationBuilder.AlterColumn<int>(
+ name: "CourseId",
+ table: "Forums",
+ nullable: true,
+ oldClrType: typeof(int),
+ oldType: "integer");
+
+ migrationBuilder.AddColumn<int>(
+ name: "SectionItemId",
+ table: "Forums",
+ nullable: false,
+ defaultValue: 0);
+
+ migrationBuilder.AddColumn<int>(
+ name: "SectionItemId",
+ table: "Files",
+ nullable: false,
+ defaultValue: 0);
+
+ migrationBuilder.CreateTable(
+ name: "SectionItemTypes",
+ columns: table => new
+ {
+ Id = table.Column<int>(nullable: false)
+ .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
+ Name = table.Column<string>(type: "character varying(100)", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_SectionItemTypes", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "Sections",
+ columns: table => new
+ {
+ Id = table.Column<int>(nullable: false)
+ .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
+ Name = table.Column<string>(type: "character varying(255)", nullable: false),
+ Order = table.Column<int>(nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Sections", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "SectionItems",
+ columns: table => new
+ {
+ Id = table.Column<int>(nullable: false)
+ .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
+ Order = table.Column<int>(nullable: false),
+ SectionId = table.Column<int>(nullable: false),
+ SectionItemTypeId = table.Column<int>(nullable: false),
+ ForumId = table.Column<int>(nullable: true),
+ FileId = table.Column<int>(nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_SectionItems", x => x.Id);
+ table.ForeignKey(
+ name: "FK_SectionItems_Files_FileId",
+ column: x => x.FileId,
+ principalTable: "Files",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Restrict);
+ table.ForeignKey(
+ name: "FK_SectionItems_Forums_ForumId",
+ column: x => x.ForumId,
+ principalTable: "Forums",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Restrict);
+ table.ForeignKey(
+ name: "FK_SectionItems_Sections_SectionId",
+ column: x => x.SectionId,
+ principalTable: "Sections",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ table.ForeignKey(
+ name: "FK_SectionItems_SectionItemTypes_SectionItemTypeId",
+ column: x => x.SectionItemTypeId,
+ principalTable: "SectionItemTypes",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Forums_CourseId",
+ table: "Forums",
+ column: "CourseId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_SectionItems_FileId",
+ table: "SectionItems",
+ column: "FileId",
+ unique: true);
+
+ migrationBuilder.CreateIndex(
+ name: "IX_SectionItems_ForumId",
+ table: "SectionItems",
+ column: "ForumId",
+ unique: true);
+
+ migrationBuilder.CreateIndex(
+ name: "IX_SectionItems_SectionId",
+ table: "SectionItems",
+ column: "SectionId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_SectionItems_SectionItemTypeId",
+ table: "SectionItems",
+ column: "SectionItemTypeId");
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Forums_Courses_CourseId",
+ table: "Forums",
+ column: "CourseId",
+ principalTable: "Courses",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Restrict);
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropForeignKey(
+ name: "FK_Forums_Courses_CourseId",
+ table: "Forums");
+
+ migrationBuilder.DropTable(
+ name: "SectionItems");
+
+ migrationBuilder.DropTable(
+ name: "Sections");
+
+ migrationBuilder.DropTable(
+ name: "SectionItemTypes");
+
+ migrationBuilder.DropIndex(
+ name: "IX_Forums_CourseId",
+ table: "Forums");
+
+ migrationBuilder.DropColumn(
+ name: "SectionItemId",
+ table: "Forums");
+
+ migrationBuilder.DropColumn(
+ name: "SectionItemId",
+ table: "Files");
+
+ migrationBuilder.AlterColumn<int>(
+ name: "CourseId",
+ table: "Forums",
+ type: "integer",
+ nullable: false,
+ oldClrType: typeof(int),
+ oldNullable: true);
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Forums_CourseId_Name",
+ table: "Forums",
+ columns: new[] { "CourseId", "Name" },
+ unique: true);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Forums_Courses_CourseId",
+ table: "Forums",
+ column: "CourseId",
+ principalTable: "Courses",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ }
+ }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/Migrations/20201107172316_sections.Designer.cs b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201107172316_sections.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..8b4eba532c5dfd4c75ef851d9d13941fd7e51edd
--- /dev/null
+++ b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201107172316_sections.Designer.cs
@@ -0,0 +1,614 @@
+// <auto-generated />
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+using Tsi1.DataLayer;
+
+namespace Tsi1.DataLayer.Migrations
+{
+ [DbContext(typeof(Tsi1Context))]
+ [Migration("20201107172316_sections")]
+ partial class sections
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
+ .HasAnnotation("ProductVersion", "3.1.4")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Connection", b =>
+ {
+ b.Property<string>("ConnectionId")
+ .HasColumnType("text");
+
+ b.Property<string>("GroupName")
+ .HasColumnType("text");
+
+ b.Property<int>("UserId")
+ .HasColumnType("integer");
+
+ b.HasKey("ConnectionId");
+
+ b.HasIndex("GroupName");
+
+ b.ToTable("Connections");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Course", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("character varying(50)");
+
+ b.Property<int>("TenantId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("TenantId");
+
+ b.HasIndex("Name", "TenantId")
+ .IsUnique();
+
+ b.ToTable("Courses");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.File", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("character varying(50)");
+
+ b.Property<string>("Path")
+ .IsRequired()
+ .HasColumnType("character varying(1000)");
+
+ b.Property<int>("SectionItemId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Path")
+ .IsUnique();
+
+ b.ToTable("Files");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Forum", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("character varying(50)");
+
+ b.Property<int>("SectionItemId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.ToTable("Forums");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.ForumUser", b =>
+ {
+ b.Property<int>("ForumId")
+ .HasColumnType("integer");
+
+ b.Property<int>("UserId")
+ .HasColumnType("integer");
+
+ b.HasKey("ForumId", "UserId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("ForumUsers");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Group", b =>
+ {
+ b.Property<string>("Name")
+ .HasColumnType("text");
+
+ b.HasKey("Name");
+
+ b.ToTable("Groups");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Post", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<DateTime>("Date")
+ .HasColumnType("timestamp without time zone");
+
+ b.Property<int>("ForumId")
+ .HasColumnType("integer");
+
+ b.Property<string>("Title")
+ .IsRequired()
+ .HasColumnType("character varying(100)");
+
+ b.Property<int>("UserId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.HasIndex("ForumId", "Title")
+ .IsUnique();
+
+ b.ToTable("Posts");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.PostMessage", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("Content")
+ .IsRequired()
+ .HasColumnType("character varying(10485760)");
+
+ b.Property<DateTime>("Date")
+ .HasColumnType("timestamp without time zone");
+
+ b.Property<int>("PostId")
+ .HasColumnType("integer");
+
+ b.Property<int>("UserId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("PostId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("PostMessages");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Professor", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("IdentityCard")
+ .IsRequired()
+ .HasColumnType("character varying(50)");
+
+ b.Property<int>("TenantId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("TenantId");
+
+ b.HasIndex("IdentityCard", "TenantId")
+ .IsUnique();
+
+ b.ToTable("Professors");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.ProfessorCourse", b =>
+ {
+ b.Property<int>("ProfessorId")
+ .HasColumnType("integer");
+
+ b.Property<int>("CourseId")
+ .HasColumnType("integer");
+
+ b.HasKey("ProfessorId", "CourseId");
+
+ b.HasIndex("CourseId");
+
+ b.ToTable("ProfessorCourses");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Section", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<int>("CourseId")
+ .HasColumnType("integer");
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("character varying(255)");
+
+ b.Property<int>("Order")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CourseId");
+
+ b.ToTable("Sections");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.SectionItem", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<int?>("FileId")
+ .HasColumnType("integer");
+
+ b.Property<int?>("ForumId")
+ .HasColumnType("integer");
+
+ b.Property<int>("Order")
+ .HasColumnType("integer");
+
+ b.Property<int>("SectionId")
+ .HasColumnType("integer");
+
+ b.Property<int>("SectionItemTypeId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("FileId")
+ .IsUnique();
+
+ b.HasIndex("ForumId")
+ .IsUnique();
+
+ b.HasIndex("SectionId");
+
+ b.HasIndex("SectionItemTypeId");
+
+ b.ToTable("SectionItems");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.SectionItemType", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("character varying(100)");
+
+ b.HasKey("Id");
+
+ b.ToTable("SectionItemTypes");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Student", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<int>("Age")
+ .HasColumnType("integer");
+
+ b.Property<string>("IdentityCard")
+ .IsRequired()
+ .HasColumnType("character varying(50)");
+
+ b.Property<int>("TenantId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("TenantId");
+
+ b.HasIndex("IdentityCard", "TenantId")
+ .IsUnique();
+
+ b.ToTable("Students");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.StudentCourse", b =>
+ {
+ b.Property<int>("StudentId")
+ .HasColumnType("integer");
+
+ b.Property<int>("CourseId")
+ .HasColumnType("integer");
+
+ b.HasKey("StudentId", "CourseId");
+
+ b.HasIndex("CourseId");
+
+ b.ToTable("StudentCourses");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Tenant", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("character varying(50)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.ToTable("Tenants");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.User", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("Email")
+ .IsRequired()
+ .HasColumnType("character varying(255)");
+
+ b.Property<string>("FirstName")
+ .IsRequired()
+ .HasColumnType("character varying(255)");
+
+ b.Property<string>("LastName")
+ .IsRequired()
+ .HasColumnType("character varying(255)");
+
+ b.Property<string>("Password")
+ .IsRequired()
+ .HasColumnType("character varying(255)");
+
+ b.Property<int?>("ProfessorId")
+ .HasColumnType("integer");
+
+ b.Property<int?>("StudentId")
+ .HasColumnType("integer");
+
+ b.Property<int>("TenantId")
+ .HasColumnType("integer");
+
+ b.Property<int>("UserTypeId")
+ .HasColumnType("integer");
+
+ b.Property<string>("Username")
+ .IsRequired()
+ .HasColumnType("character varying(50)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ProfessorId")
+ .IsUnique();
+
+ b.HasIndex("StudentId")
+ .IsUnique();
+
+ b.HasIndex("TenantId");
+
+ b.HasIndex("UserTypeId");
+
+ b.HasIndex("Username", "TenantId")
+ .IsUnique();
+
+ b.ToTable("Users");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.UserType", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("character varying(50)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.ToTable("UserTypes");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Connection", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Group", "Group")
+ .WithMany("Connections")
+ .HasForeignKey("GroupName");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Course", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
+ .WithMany("Courses")
+ .HasForeignKey("TenantId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.ForumUser", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Forum", "Forum")
+ .WithMany("ForumUsers")
+ .HasForeignKey("ForumId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tsi1.DataLayer.Entities.User", "User")
+ .WithMany("ForumUsers")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Post", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Forum", "Forum")
+ .WithMany("Posts")
+ .HasForeignKey("ForumId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tsi1.DataLayer.Entities.User", "User")
+ .WithMany("Posts")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.PostMessage", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Post", "Post")
+ .WithMany("PostMessages")
+ .HasForeignKey("PostId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tsi1.DataLayer.Entities.User", "User")
+ .WithMany("PostMessages")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Professor", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
+ .WithMany("Professors")
+ .HasForeignKey("TenantId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.ProfessorCourse", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Course", "Course")
+ .WithMany("ProfessorCourses")
+ .HasForeignKey("CourseId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tsi1.DataLayer.Entities.Professor", "Professor")
+ .WithMany("ProfessorCourses")
+ .HasForeignKey("ProfessorId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Section", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Course", "Course")
+ .WithMany("Sections")
+ .HasForeignKey("CourseId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.SectionItem", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.File", "File")
+ .WithOne("SectionItem")
+ .HasForeignKey("Tsi1.DataLayer.Entities.SectionItem", "FileId");
+
+ b.HasOne("Tsi1.DataLayer.Entities.Forum", "Forum")
+ .WithOne("SectionItem")
+ .HasForeignKey("Tsi1.DataLayer.Entities.SectionItem", "ForumId");
+
+ b.HasOne("Tsi1.DataLayer.Entities.Section", "Section")
+ .WithMany("SectionItems")
+ .HasForeignKey("SectionId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tsi1.DataLayer.Entities.SectionItemType", "SectionItemType")
+ .WithMany("SectionItems")
+ .HasForeignKey("SectionItemTypeId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Student", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
+ .WithMany("Students")
+ .HasForeignKey("TenantId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.StudentCourse", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Course", "Course")
+ .WithMany("StudentCourses")
+ .HasForeignKey("CourseId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tsi1.DataLayer.Entities.Student", "Student")
+ .WithMany("StudentCourses")
+ .HasForeignKey("StudentId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.User", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Professor", "Professor")
+ .WithOne("User")
+ .HasForeignKey("Tsi1.DataLayer.Entities.User", "ProfessorId");
+
+ b.HasOne("Tsi1.DataLayer.Entities.Student", "Student")
+ .WithOne("User")
+ .HasForeignKey("Tsi1.DataLayer.Entities.User", "StudentId");
+
+ b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
+ .WithMany("Users")
+ .HasForeignKey("TenantId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tsi1.DataLayer.Entities.UserType", "UserType")
+ .WithMany()
+ .HasForeignKey("UserTypeId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/Migrations/20201107172316_sections.cs b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201107172316_sections.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5166873d947bef5f921335cb75be228b0314b415
--- /dev/null
+++ b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201107172316_sections.cs
@@ -0,0 +1,75 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+namespace Tsi1.DataLayer.Migrations
+{
+ public partial class sections : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropForeignKey(
+ name: "FK_Forums_Courses_CourseId",
+ table: "Forums");
+
+ migrationBuilder.DropIndex(
+ name: "IX_Forums_CourseId",
+ table: "Forums");
+
+ migrationBuilder.DropColumn(
+ name: "CourseId",
+ table: "Forums");
+
+ migrationBuilder.AddColumn<int>(
+ name: "CourseId",
+ table: "Sections",
+ nullable: false,
+ defaultValue: 0);
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Sections_CourseId",
+ table: "Sections",
+ column: "CourseId");
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Sections_Courses_CourseId",
+ table: "Sections",
+ column: "CourseId",
+ principalTable: "Courses",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropForeignKey(
+ name: "FK_Sections_Courses_CourseId",
+ table: "Sections");
+
+ migrationBuilder.DropIndex(
+ name: "IX_Sections_CourseId",
+ table: "Sections");
+
+ migrationBuilder.DropColumn(
+ name: "CourseId",
+ table: "Sections");
+
+ migrationBuilder.AddColumn<int>(
+ name: "CourseId",
+ table: "Forums",
+ type: "integer",
+ nullable: true);
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Forums_CourseId",
+ table: "Forums",
+ column: "CourseId");
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Forums_Courses_CourseId",
+ table: "Forums",
+ column: "CourseId",
+ principalTable: "Courses",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Restrict);
+ }
+ }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/Migrations/20201107181002_fix-section-items.Designer.cs b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201107181002_fix-section-items.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..27f320d9ce062b4e979b01b4f86858827274e92b
--- /dev/null
+++ b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201107181002_fix-section-items.Designer.cs
@@ -0,0 +1,608 @@
+// <auto-generated />
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+using Tsi1.DataLayer;
+
+namespace Tsi1.DataLayer.Migrations
+{
+ [DbContext(typeof(Tsi1Context))]
+ [Migration("20201107181002_fix-section-items")]
+ partial class fixsectionitems
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
+ .HasAnnotation("ProductVersion", "3.1.4")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Connection", b =>
+ {
+ b.Property<string>("ConnectionId")
+ .HasColumnType("text");
+
+ b.Property<string>("GroupName")
+ .HasColumnType("text");
+
+ b.Property<int>("UserId")
+ .HasColumnType("integer");
+
+ b.HasKey("ConnectionId");
+
+ b.HasIndex("GroupName");
+
+ b.ToTable("Connections");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Course", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("character varying(50)");
+
+ b.Property<int>("TenantId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("TenantId");
+
+ b.HasIndex("Name", "TenantId")
+ .IsUnique();
+
+ b.ToTable("Courses");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.File", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("character varying(50)");
+
+ b.Property<string>("Path")
+ .IsRequired()
+ .HasColumnType("character varying(1000)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Path")
+ .IsUnique();
+
+ b.ToTable("Files");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Forum", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("character varying(50)");
+
+ b.HasKey("Id");
+
+ b.ToTable("Forums");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.ForumUser", b =>
+ {
+ b.Property<int>("ForumId")
+ .HasColumnType("integer");
+
+ b.Property<int>("UserId")
+ .HasColumnType("integer");
+
+ b.HasKey("ForumId", "UserId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("ForumUsers");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Group", b =>
+ {
+ b.Property<string>("Name")
+ .HasColumnType("text");
+
+ b.HasKey("Name");
+
+ b.ToTable("Groups");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Post", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<DateTime>("Date")
+ .HasColumnType("timestamp without time zone");
+
+ b.Property<int>("ForumId")
+ .HasColumnType("integer");
+
+ b.Property<string>("Title")
+ .IsRequired()
+ .HasColumnType("character varying(100)");
+
+ b.Property<int>("UserId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.HasIndex("ForumId", "Title")
+ .IsUnique();
+
+ b.ToTable("Posts");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.PostMessage", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("Content")
+ .IsRequired()
+ .HasColumnType("character varying(10485760)");
+
+ b.Property<DateTime>("Date")
+ .HasColumnType("timestamp without time zone");
+
+ b.Property<int>("PostId")
+ .HasColumnType("integer");
+
+ b.Property<int>("UserId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("PostId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("PostMessages");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Professor", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("IdentityCard")
+ .IsRequired()
+ .HasColumnType("character varying(50)");
+
+ b.Property<int>("TenantId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("TenantId");
+
+ b.HasIndex("IdentityCard", "TenantId")
+ .IsUnique();
+
+ b.ToTable("Professors");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.ProfessorCourse", b =>
+ {
+ b.Property<int>("ProfessorId")
+ .HasColumnType("integer");
+
+ b.Property<int>("CourseId")
+ .HasColumnType("integer");
+
+ b.HasKey("ProfessorId", "CourseId");
+
+ b.HasIndex("CourseId");
+
+ b.ToTable("ProfessorCourses");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Section", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<int>("CourseId")
+ .HasColumnType("integer");
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("character varying(255)");
+
+ b.Property<int>("Order")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CourseId");
+
+ b.ToTable("Sections");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.SectionItem", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<int?>("FileId")
+ .HasColumnType("integer");
+
+ b.Property<int?>("ForumId")
+ .HasColumnType("integer");
+
+ b.Property<int>("Order")
+ .HasColumnType("integer");
+
+ b.Property<int>("SectionId")
+ .HasColumnType("integer");
+
+ b.Property<int>("SectionItemTypeId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("FileId")
+ .IsUnique();
+
+ b.HasIndex("ForumId")
+ .IsUnique();
+
+ b.HasIndex("SectionId");
+
+ b.HasIndex("SectionItemTypeId");
+
+ b.ToTable("SectionItems");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.SectionItemType", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("character varying(100)");
+
+ b.HasKey("Id");
+
+ b.ToTable("SectionItemTypes");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Student", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<int>("Age")
+ .HasColumnType("integer");
+
+ b.Property<string>("IdentityCard")
+ .IsRequired()
+ .HasColumnType("character varying(50)");
+
+ b.Property<int>("TenantId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("TenantId");
+
+ b.HasIndex("IdentityCard", "TenantId")
+ .IsUnique();
+
+ b.ToTable("Students");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.StudentCourse", b =>
+ {
+ b.Property<int>("StudentId")
+ .HasColumnType("integer");
+
+ b.Property<int>("CourseId")
+ .HasColumnType("integer");
+
+ b.HasKey("StudentId", "CourseId");
+
+ b.HasIndex("CourseId");
+
+ b.ToTable("StudentCourses");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Tenant", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("character varying(50)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.ToTable("Tenants");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.User", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("Email")
+ .IsRequired()
+ .HasColumnType("character varying(255)");
+
+ b.Property<string>("FirstName")
+ .IsRequired()
+ .HasColumnType("character varying(255)");
+
+ b.Property<string>("LastName")
+ .IsRequired()
+ .HasColumnType("character varying(255)");
+
+ b.Property<string>("Password")
+ .IsRequired()
+ .HasColumnType("character varying(255)");
+
+ b.Property<int?>("ProfessorId")
+ .HasColumnType("integer");
+
+ b.Property<int?>("StudentId")
+ .HasColumnType("integer");
+
+ b.Property<int>("TenantId")
+ .HasColumnType("integer");
+
+ b.Property<int>("UserTypeId")
+ .HasColumnType("integer");
+
+ b.Property<string>("Username")
+ .IsRequired()
+ .HasColumnType("character varying(50)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ProfessorId")
+ .IsUnique();
+
+ b.HasIndex("StudentId")
+ .IsUnique();
+
+ b.HasIndex("TenantId");
+
+ b.HasIndex("UserTypeId");
+
+ b.HasIndex("Username", "TenantId")
+ .IsUnique();
+
+ b.ToTable("Users");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.UserType", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("character varying(50)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.ToTable("UserTypes");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Connection", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Group", "Group")
+ .WithMany("Connections")
+ .HasForeignKey("GroupName");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Course", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
+ .WithMany("Courses")
+ .HasForeignKey("TenantId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.ForumUser", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Forum", "Forum")
+ .WithMany("ForumUsers")
+ .HasForeignKey("ForumId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tsi1.DataLayer.Entities.User", "User")
+ .WithMany("ForumUsers")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Post", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Forum", "Forum")
+ .WithMany("Posts")
+ .HasForeignKey("ForumId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tsi1.DataLayer.Entities.User", "User")
+ .WithMany("Posts")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.PostMessage", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Post", "Post")
+ .WithMany("PostMessages")
+ .HasForeignKey("PostId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tsi1.DataLayer.Entities.User", "User")
+ .WithMany("PostMessages")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Professor", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
+ .WithMany("Professors")
+ .HasForeignKey("TenantId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.ProfessorCourse", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Course", "Course")
+ .WithMany("ProfessorCourses")
+ .HasForeignKey("CourseId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tsi1.DataLayer.Entities.Professor", "Professor")
+ .WithMany("ProfessorCourses")
+ .HasForeignKey("ProfessorId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Section", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Course", "Course")
+ .WithMany("Sections")
+ .HasForeignKey("CourseId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.SectionItem", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.File", "File")
+ .WithOne("SectionItem")
+ .HasForeignKey("Tsi1.DataLayer.Entities.SectionItem", "FileId");
+
+ b.HasOne("Tsi1.DataLayer.Entities.Forum", "Forum")
+ .WithOne("SectionItem")
+ .HasForeignKey("Tsi1.DataLayer.Entities.SectionItem", "ForumId");
+
+ b.HasOne("Tsi1.DataLayer.Entities.Section", "Section")
+ .WithMany("SectionItems")
+ .HasForeignKey("SectionId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tsi1.DataLayer.Entities.SectionItemType", "SectionItemType")
+ .WithMany("SectionItems")
+ .HasForeignKey("SectionItemTypeId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Student", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
+ .WithMany("Students")
+ .HasForeignKey("TenantId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.StudentCourse", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Course", "Course")
+ .WithMany("StudentCourses")
+ .HasForeignKey("CourseId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tsi1.DataLayer.Entities.Student", "Student")
+ .WithMany("StudentCourses")
+ .HasForeignKey("StudentId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.User", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Professor", "Professor")
+ .WithOne("User")
+ .HasForeignKey("Tsi1.DataLayer.Entities.User", "ProfessorId");
+
+ b.HasOne("Tsi1.DataLayer.Entities.Student", "Student")
+ .WithOne("User")
+ .HasForeignKey("Tsi1.DataLayer.Entities.User", "StudentId");
+
+ b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
+ .WithMany("Users")
+ .HasForeignKey("TenantId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tsi1.DataLayer.Entities.UserType", "UserType")
+ .WithMany()
+ .HasForeignKey("UserTypeId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/Migrations/20201107181002_fix-section-items.cs b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201107181002_fix-section-items.cs
new file mode 100644
index 0000000000000000000000000000000000000000..8c62edd2ca588155db26ae64cdef48b67efdc034
--- /dev/null
+++ b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201107181002_fix-section-items.cs
@@ -0,0 +1,35 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+namespace Tsi1.DataLayer.Migrations
+{
+ public partial class fixsectionitems : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "SectionItemId",
+ table: "Forums");
+
+ migrationBuilder.DropColumn(
+ name: "SectionItemId",
+ table: "Files");
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn<int>(
+ name: "SectionItemId",
+ table: "Forums",
+ type: "integer",
+ nullable: false,
+ defaultValue: 0);
+
+ migrationBuilder.AddColumn<int>(
+ name: "SectionItemId",
+ table: "Files",
+ type: "integer",
+ nullable: false,
+ defaultValue: 0);
+ }
+ }
+}
diff --git a/Tsi1.Api/Tsi1.DataLayer/Migrations/Tsi1ContextModelSnapshot.cs b/Tsi1.Api/Tsi1.DataLayer/Migrations/Tsi1ContextModelSnapshot.cs
index 9f2b80da778b9cfc5bb392f6faa481cab6f4eea7..e661f62fc22750b698b1da14d4ac889d01af8e80 100644
--- a/Tsi1.Api/Tsi1.DataLayer/Migrations/Tsi1ContextModelSnapshot.cs
+++ b/Tsi1.Api/Tsi1.DataLayer/Migrations/Tsi1ContextModelSnapshot.cs
@@ -91,18 +91,12 @@ namespace Tsi1.DataLayer.Migrations
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
- b.Property<int>("CourseId")
- .HasColumnType("integer");
-
b.Property<string>("Name")
.IsRequired()
.HasColumnType("character varying(50)");
b.HasKey("Id");
- b.HasIndex("CourseId", "Name")
- .IsUnique();
-
b.ToTable("Forums");
});
@@ -229,6 +223,83 @@ namespace Tsi1.DataLayer.Migrations
b.ToTable("ProfessorCourses");
});
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Section", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<int>("CourseId")
+ .HasColumnType("integer");
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("character varying(255)");
+
+ b.Property<int>("Order")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CourseId");
+
+ b.ToTable("Sections");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.SectionItem", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<int?>("FileId")
+ .HasColumnType("integer");
+
+ b.Property<int?>("ForumId")
+ .HasColumnType("integer");
+
+ b.Property<int>("Order")
+ .HasColumnType("integer");
+
+ b.Property<int>("SectionId")
+ .HasColumnType("integer");
+
+ b.Property<int>("SectionItemTypeId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("FileId")
+ .IsUnique();
+
+ b.HasIndex("ForumId")
+ .IsUnique();
+
+ b.HasIndex("SectionId");
+
+ b.HasIndex("SectionItemTypeId");
+
+ b.ToTable("SectionItems");
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.SectionItemType", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("character varying(100)");
+
+ b.HasKey("Id");
+
+ b.ToTable("SectionItemTypes");
+ });
+
modelBuilder.Entity("Tsi1.DataLayer.Entities.Student", b =>
{
b.Property<int>("Id")
@@ -382,15 +453,6 @@ namespace Tsi1.DataLayer.Migrations
.IsRequired();
});
- modelBuilder.Entity("Tsi1.DataLayer.Entities.Forum", b =>
- {
- b.HasOne("Tsi1.DataLayer.Entities.Course", "Course")
- .WithMany("Forums")
- .HasForeignKey("CourseId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
modelBuilder.Entity("Tsi1.DataLayer.Entities.ForumUser", b =>
{
b.HasOne("Tsi1.DataLayer.Entities.Forum", "Forum")
@@ -460,6 +522,38 @@ namespace Tsi1.DataLayer.Migrations
.IsRequired();
});
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.Section", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.Course", "Course")
+ .WithMany("Sections")
+ .HasForeignKey("CourseId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Tsi1.DataLayer.Entities.SectionItem", b =>
+ {
+ b.HasOne("Tsi1.DataLayer.Entities.File", "File")
+ .WithOne("SectionItem")
+ .HasForeignKey("Tsi1.DataLayer.Entities.SectionItem", "FileId");
+
+ b.HasOne("Tsi1.DataLayer.Entities.Forum", "Forum")
+ .WithOne("SectionItem")
+ .HasForeignKey("Tsi1.DataLayer.Entities.SectionItem", "ForumId");
+
+ b.HasOne("Tsi1.DataLayer.Entities.Section", "Section")
+ .WithMany("SectionItems")
+ .HasForeignKey("SectionId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tsi1.DataLayer.Entities.SectionItemType", "SectionItemType")
+ .WithMany("SectionItems")
+ .HasForeignKey("SectionItemTypeId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
modelBuilder.Entity("Tsi1.DataLayer.Entities.Student", b =>
{
b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant")
diff --git a/Tsi1.Api/Tsi1.DataLayer/Tsi1Context.cs b/Tsi1.Api/Tsi1.DataLayer/Tsi1Context.cs
index d442517b50b82f0ebd6f86812ac69f463cd16239..ba1524c725417ba94a5672a0254a9cd05d66f911 100644
--- a/Tsi1.Api/Tsi1.DataLayer/Tsi1Context.cs
+++ b/Tsi1.Api/Tsi1.DataLayer/Tsi1Context.cs
@@ -24,6 +24,9 @@ namespace Tsi1.DataLayer
public DbSet<Group> Groups { get; set; }
public DbSet<Connection> Connections { get; set; }
public DbSet<File> Files { get; set; }
+ public DbSet<Section> Sections { get; set; }
+ public DbSet<SectionItem> SectionItems { get; set; }
+ public DbSet<SectionItemType> SectionItemTypes { get; set; }
public Tsi1Context(DbContextOptions options) : base(options) { }
@@ -45,6 +48,9 @@ namespace Tsi1.DataLayer
modelBuilder.ApplyConfiguration(new GroupConfiguration());
modelBuilder.ApplyConfiguration(new ConnectionConfiguration());
modelBuilder.ApplyConfiguration(new FileConfiguration());
+ modelBuilder.ApplyConfiguration(new SectionConfiguration());
+ modelBuilder.ApplyConfiguration(new SectionItemConfiguration());
+ modelBuilder.ApplyConfiguration(new SectionItemTypeConfiguration());
}
}
}