├── src ├── Apps │ ├── TORSHIA.App │ │ ├── Views │ │ │ ├── Shared │ │ │ │ ├── _Error.html │ │ │ │ ├── DisplayTemplates │ │ │ │ │ ├── IndexTasksRowViewModelDisplayTemplate.html │ │ │ │ │ ├── AllReportViewModelDisplayTemplate.html │ │ │ │ │ ├── IndexTaskViewModelDisplayTemplate.html │ │ │ │ │ ├── DetailsTaskViewModelDisplayTemplate.html │ │ │ │ │ └── DetailsReportViewModelDisplayTemplate.html │ │ │ │ └── _Layout.html │ │ │ ├── Tasks │ │ │ │ ├── Details.html │ │ │ │ └── Create.html │ │ │ ├── Reports │ │ │ │ ├── Details.html │ │ │ │ └── All.html │ │ │ ├── Home │ │ │ │ ├── Index-Admin.html │ │ │ │ ├── Index-User.html │ │ │ │ └── Index.html │ │ │ └── Users │ │ │ │ ├── Login.html │ │ │ │ └── Register.html │ │ ├── Resources │ │ │ ├── favicon.ico │ │ │ ├── style.css │ │ │ └── reset-css.css │ │ ├── Launcher.cs │ │ ├── TORSHIA.App.csproj │ │ ├── Controllers │ │ │ ├── BaseController.cs │ │ │ ├── HomeController.cs │ │ │ ├── UsersController.cs │ │ │ ├── ReportsController.cs │ │ │ └── TasksController.cs │ │ └── StartUp.cs │ ├── TORSHIA.Common │ │ ├── TORSHIA.Common.csproj │ │ └── Models │ │ │ ├── Binding │ │ │ ├── LoginUserBindingModel.cs │ │ │ ├── RegisterUserBindingModel.cs │ │ │ └── CreateTaskBindingModel.cs │ │ │ └── View │ │ │ ├── IndexTaskViewModel.cs │ │ │ ├── AllReportViewModel.cs │ │ │ ├── DetailsTaskViewModel.cs │ │ │ ├── IndexTasksRowViewModel.cs │ │ │ └── DetailsReportViewModel.cs │ ├── TORSHIA.Domain │ │ ├── Sector.cs │ │ ├── UserRole.cs │ │ ├── ReportStatus.cs │ │ ├── TORSHIA.Domain.csproj │ │ ├── TaskSector.cs │ │ ├── User.cs │ │ ├── Report.cs │ │ └── Task.cs │ ├── TORSHIA.Services │ │ ├── Contracts │ │ │ ├── ISectorsService.cs │ │ │ ├── IUsersService.cs │ │ │ ├── IReportsService.cs │ │ │ └── ITasksService.cs │ │ ├── TORSHIA.Services.csproj │ │ ├── SectorService.cs │ │ ├── UsersService.cs │ │ ├── ReportsService.cs │ │ └── TasksService.cs │ └── TORSHIA.Data │ │ ├── EntityConfiguration │ │ ├── TaskEntityConfiguration.cs │ │ ├── SectorEntityConfiguration.cs │ │ ├── UserRoleEntityConfiguration.cs │ │ ├── ReportStatusEntityConfiguration.cs │ │ ├── UserEntityConfiguration.cs │ │ ├── TaskSectorEntityConfiguration.cs │ │ └── ReportEntityConfiguration.cs │ │ ├── TORSHIA.Data.csproj │ │ └── TorshiaDbContext.cs ├── SIS.HTTP │ ├── SIS.HTTP.csproj │ ├── Enums │ │ ├── HttpRequestMethod.cs │ │ └── HttpResponseStatusCode.cs │ ├── Headers │ │ ├── IHttpHeaderCollection.cs │ │ ├── HttpHeader.cs │ │ └── HttpHeaderCollection.cs │ ├── Common │ │ ├── GlobalConstants.cs │ │ └── CoreValidator.cs │ ├── Sessions │ │ ├── IHttpSession.cs │ │ ├── HttpSessionStorage.cs │ │ └── HttpSession.cs │ ├── Exceptions │ │ ├── BadRequestException.cs │ │ └── InternalServerErrorException.cs │ ├── Cookies │ │ ├── IHttpCookieCollection.cs │ │ ├── HttpCookieCollection.cs │ │ └── HttpCookie.cs │ ├── Extensions │ │ ├── StringExtensions.cs │ │ └── HttpResponseStatusExtensions.cs │ ├── Responses │ │ ├── IHttpResponse.cs │ │ └── HttpResponse.cs │ └── Requests │ │ ├── IHttpRequest.cs │ │ └── HttpRequest.cs ├── SIS.Framework │ ├── ActionResults │ │ ├── Interfaces │ │ │ ├── IRenderable.cs │ │ │ ├── IActionResult.cs │ │ │ ├── IViewable.cs │ │ │ └── IRedirectable.cs │ │ └── Implementations │ │ │ ├── ViewResult.cs │ │ │ └── RedirectResult.cs │ ├── Routers │ │ ├── Contracts │ │ │ ├── IMvcrouter.cs │ │ │ ├── IResourceRouter.cs │ │ │ └── ICustomRouter.cs │ │ ├── CustomRouter.cs │ │ ├── ResourceRouter.cs │ │ └── ControllerRouter.cs │ ├── Api │ │ ├── IMvcApplication.cs │ │ └── MvcApplication.cs │ ├── Models │ │ ├── Model.cs │ │ └── ViewModel.cs │ ├── Services │ │ ├── IDependencyContainer.cs │ │ └── DependencyContainer.cs │ ├── Utilities │ │ └── ControllerUtilities.cs │ ├── Security │ │ ├── IIdentity.cs │ │ └── IdentityUser.cs │ ├── Attributes │ │ ├── Method │ │ │ ├── HttpGetAttribute.cs │ │ │ ├── HttpPostAttribute.cs │ │ │ ├── HttpPutAttribute.cs │ │ │ ├── HttpDeleteAttribute.cs │ │ │ └── HttpMethodAttribute.cs │ │ ├── Property │ │ │ ├── RegexAttribute.cs │ │ │ └── NumberRangeAttribute.cs │ │ └── Action │ │ │ └── AuthorizeAttribute.cs │ ├── SIS.Framework.csproj │ ├── Views │ │ ├── View.cs │ │ └── ViewEngine.cs │ ├── MvcContext.cs │ ├── WebHost.cs │ ├── HttpRequestHandlingContext.cs │ └── Controllers │ │ └── Controller.cs ├── SIS.WebServer │ ├── Api │ │ └── IHttpRequestHandler.cs │ ├── SIS.WebServer.csproj │ ├── Results │ │ ├── RedirectResult.cs │ │ ├── FileResult.cs │ │ ├── HtmlResult.cs │ │ ├── TextResult.cs │ │ ├── InlineResouceResult.cs │ │ ├── UnauthorizedResult.cs │ │ ├── BadRequestResult.cs │ │ └── InternalServerErrorResult.cs │ ├── Server.cs │ └── ConnectionHandler.cs └── SIS.sln ├── README.md ├── LICENSE └── .gitignore /src/Apps/TORSHIA.App/Views/Shared/_Error.html: -------------------------------------------------------------------------------- 1 |

@RenderError()

-------------------------------------------------------------------------------- /src/Apps/TORSHIA.App/Resources/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoftUni-CSharp-Web-Development/SIS/HEAD/src/Apps/TORSHIA.App/Resources/favicon.ico -------------------------------------------------------------------------------- /src/SIS.HTTP/SIS.HTTP.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/SIS.Framework/ActionResults/Interfaces/IRenderable.cs: -------------------------------------------------------------------------------- 1 | namespace SIS.Framework.ActionResults 2 | { 3 | public interface IRenderable 4 | { 5 | string Render(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/SIS.Framework/ActionResults/Interfaces/IActionResult.cs: -------------------------------------------------------------------------------- 1 | namespace SIS.Framework.ActionResults 2 | { 3 | public interface IActionResult 4 | { 5 | string Invoke(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Common/TORSHIA.Common.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/SIS.HTTP/Enums/HttpRequestMethod.cs: -------------------------------------------------------------------------------- 1 | namespace SIS.HTTP.Enums 2 | { 3 | public enum HttpRequestMethod 4 | { 5 | Get, 6 | Post, 7 | Put, 8 | Delete 9 | } 10 | } -------------------------------------------------------------------------------- /src/SIS.Framework/ActionResults/Interfaces/IViewable.cs: -------------------------------------------------------------------------------- 1 | namespace SIS.Framework.ActionResults 2 | { 3 | public interface IViewable : IActionResult 4 | { 5 | IRenderable View { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/SIS.Framework/Routers/Contracts/IMvcrouter.cs: -------------------------------------------------------------------------------- 1 | using SIS.WebServer.Api; 2 | 3 | namespace SIS.Framework.Routers.Contracts 4 | { 5 | public interface IMvcRouter : IHttpRequestHandler 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Domain/Sector.cs: -------------------------------------------------------------------------------- 1 | namespace TORSHIA.Domain 2 | { 3 | public class Sector 4 | { 5 | public string Id { get; set; } 6 | 7 | public string Name { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/SIS.Framework/ActionResults/Interfaces/IRedirectable.cs: -------------------------------------------------------------------------------- 1 | namespace SIS.Framework.ActionResults 2 | { 3 | public interface IRedirectable : IActionResult 4 | { 5 | string RedirectUrl { get; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Domain/UserRole.cs: -------------------------------------------------------------------------------- 1 | namespace TORSHIA.Domain 2 | { 3 | public class UserRole 4 | { 5 | public string Id { get; set; } 6 | 7 | public string Name { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.App/Views/Tasks/Details.html: -------------------------------------------------------------------------------- 1 | @Model.Task 2 |
3 |
4 | Back 5 |
-------------------------------------------------------------------------------- /src/Apps/TORSHIA.Domain/ReportStatus.cs: -------------------------------------------------------------------------------- 1 | namespace TORSHIA.Domain 2 | { 3 | public class ReportStatus 4 | { 5 | public string Id { get; set; } 6 | 7 | public string Status { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.App/Views/Reports/Details.html: -------------------------------------------------------------------------------- 1 | @Model.Report 2 |
3 |
4 | Back 5 |
-------------------------------------------------------------------------------- /src/Apps/TORSHIA.Services/Contracts/ISectorsService.cs: -------------------------------------------------------------------------------- 1 | using TORSHIA.Domain; 2 | 3 | namespace TORSHIA.Services.Contracts 4 | { 5 | public interface ISectorsService 6 | { 7 | Sector GetSectorById(string sectorId); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.App/Views/Shared/DisplayTemplates/IndexTasksRowViewModelDisplayTemplate.html: -------------------------------------------------------------------------------- 1 |
2 | @Model.Collection.Tasks(@Item) 3 | @Model.Collection.Empty(
) 4 |
-------------------------------------------------------------------------------- /src/Apps/TORSHIA.App/Launcher.cs: -------------------------------------------------------------------------------- 1 | using SIS.Framework; 2 | 3 | namespace TORSHIA.App 4 | { 5 | public class Launcher 6 | { 7 | static void Main(string[] args) 8 | { 9 | WebHost.Start(new StartUp()); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/SIS.WebServer/Api/IHttpRequestHandler.cs: -------------------------------------------------------------------------------- 1 | using SIS.HTTP.Requests; 2 | using SIS.HTTP.Responses; 3 | 4 | namespace SIS.WebServer.Api 5 | { 6 | public interface IHttpRequestHandler 7 | { 8 | IHttpResponse Handle(IHttpRequest request); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Common/Models/Binding/LoginUserBindingModel.cs: -------------------------------------------------------------------------------- 1 | namespace TORSHIA.App.Models.Binding 2 | { 3 | public class LoginUserBindingModel 4 | { 5 | public string Username { get; set; } 6 | 7 | public string Password { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.App/Views/Home/Index-Admin.html: -------------------------------------------------------------------------------- 1 |

Welcome, Admin-@Model.Username!

2 |

Enjoy your work!

3 |
4 |
5 | @Model.Collection.TaskRows(@Item) 6 |
-------------------------------------------------------------------------------- /src/Apps/TORSHIA.App/Views/Home/Index-User.html: -------------------------------------------------------------------------------- 1 |

Welcome, Admin-@Model.Username!

2 |

Enjoy your work!

3 |
4 |
5 | @Model.Collection.TaskRows(@Item) 6 |
-------------------------------------------------------------------------------- /src/SIS.Framework/Routers/Contracts/IResourceRouter.cs: -------------------------------------------------------------------------------- 1 | using SIS.WebServer.Api; 2 | 3 | namespace SIS.Framework.Routers.Contracts 4 | { 5 | public interface IResourceRouter : IHttpRequestHandler 6 | { 7 | bool IsResourceRequest(string httpRequestPath); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/SIS.HTTP/Headers/IHttpHeaderCollection.cs: -------------------------------------------------------------------------------- 1 | namespace SIS.HTTP.Headers 2 | { 3 | public interface IHttpHeaderCollection 4 | { 5 | void Add(HttpHeader header); 6 | 7 | bool ContainsHeader(string key); 8 | 9 | HttpHeader GetHeader(string key); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/SIS.Framework/Api/IMvcApplication.cs: -------------------------------------------------------------------------------- 1 | using SIS.Framework.Services; 2 | 3 | namespace SIS.Framework.Api 4 | { 5 | public interface IMvcApplication 6 | { 7 | void Configure(); 8 | 9 | void ConfigureServices(IDependencyContainer dependencyContainer); 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Common/Models/View/IndexTaskViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace TORSHIA.App.Models.View 2 | { 3 | public class IndexTaskViewModel 4 | { 5 | public string Id { get; set; } 6 | 7 | public string Title { get; set; } 8 | 9 | public int Level { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/SIS.Framework/Routers/Contracts/ICustomRouter.cs: -------------------------------------------------------------------------------- 1 | using SIS.HTTP.Requests; 2 | using SIS.WebServer.Api; 3 | 4 | namespace SIS.Framework.Routers.Contracts 5 | { 6 | public interface ICustomRouter : IHttpRequestHandler 7 | { 8 | bool ContainsMapping(IHttpRequest httpRequest); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/SIS.WebServer/SIS.WebServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/SIS.Framework/Models/Model.cs: -------------------------------------------------------------------------------- 1 | namespace SIS.Framework.Models 2 | { 3 | public class Model 4 | { 5 | private bool? isValid; 6 | 7 | public bool? IsValid 8 | { 9 | get => this.isValid; 10 | set => this.isValid = this.isValid ?? value; 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Domain/TORSHIA.Domain.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Domain/TaskSector.cs: -------------------------------------------------------------------------------- 1 | namespace TORSHIA.Domain 2 | { 3 | public class TaskSector 4 | { 5 | public string TaskId { get; set; } 6 | 7 | public Task Task { get; set; } 8 | 9 | public string SectorId { get; set; } 10 | 11 | public Sector Sector { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/SIS.HTTP/Common/GlobalConstants.cs: -------------------------------------------------------------------------------- 1 | namespace SIS.HTTP.Common 2 | { 3 | public static class GlobalConstants 4 | { 5 | public const string HttpOneProtocolFragment = "HTTP/1.1"; 6 | 7 | public const string HostHeaderKey = "Host"; 8 | 9 | public const string HttpNewLine = "\r\n"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/SIS.Framework/Api/MvcApplication.cs: -------------------------------------------------------------------------------- 1 | using SIS.Framework.Services; 2 | 3 | namespace SIS.Framework.Api 4 | { 5 | public class MvcApplication : IMvcApplication 6 | { 7 | public virtual void Configure() { } 8 | 9 | public virtual void ConfigureServices(IDependencyContainer dependencyContainer) { } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/SIS.Framework/Services/IDependencyContainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SIS.Framework.Services 4 | { 5 | public interface IDependencyContainer 6 | { 7 | void RegisterDependency(); 8 | 9 | T CreateInstance(); 10 | 11 | object CreateInstance(Type type); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/SIS.Framework/Utilities/ControllerUtilities.cs: -------------------------------------------------------------------------------- 1 | namespace SIS.Framework.Utilities 2 | { 3 | public class ControllerUtilities 4 | { 5 | public static string GetControllerName(object controller) 6 | => controller.GetType() 7 | .Name.Replace(MvcContext.Get.ControllersSuffix, string.Empty); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.App/Views/Home/Index.html: -------------------------------------------------------------------------------- 1 |
2 |

Welcome to TORSHIA Task Management.

3 |
4 |

Login if you have an account.

5 |

Register if you don't.

6 |
-------------------------------------------------------------------------------- /src/Apps/TORSHIA.Services/Contracts/IUsersService.cs: -------------------------------------------------------------------------------- 1 | using TORSHIA.App.Models.Binding; 2 | using TORSHIA.Domain; 3 | 4 | namespace TORSHIA.Services.Contracts 5 | { 6 | public interface IUsersService 7 | { 8 | void CreateUser(RegisterUserBindingModel registerUserBindingModel); 9 | 10 | User GetUserByUsername(string username); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SoftUni Information Services - the IIS wannabe 2 | **SoftUni Information Services** is a package of web services, including an **HTTP Server** and an **MVC Framework**, written both in C#. These web services are for educational purposes. They are mimicking the IIS and ASP.NET, and are used for the 3 | **C# Web Development Basics Course** @ [**SoftUni**](https://softuni.bg) 4 | -------------------------------------------------------------------------------- /src/SIS.HTTP/Sessions/IHttpSession.cs: -------------------------------------------------------------------------------- 1 | namespace SIS.HTTP.Sessions 2 | { 3 | public interface IHttpSession 4 | { 5 | string Id { get; } 6 | 7 | object GetParameter(string name); 8 | 9 | bool ContainsParameter(string name); 10 | 11 | void AddParameter(string name, object parameter); 12 | 13 | void ClearParameters(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Common/Models/Binding/RegisterUserBindingModel.cs: -------------------------------------------------------------------------------- 1 | namespace TORSHIA.App.Models.Binding 2 | { 3 | public class RegisterUserBindingModel 4 | { 5 | public string Username { get; set; } 6 | 7 | public string Password { get; set; } 8 | 9 | public string ConfirmPassword { get; set; } 10 | 11 | public string Email { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/SIS.HTTP/Enums/HttpResponseStatusCode.cs: -------------------------------------------------------------------------------- 1 | namespace SIS.HTTP.Enums 2 | { 3 | public enum HttpResponseStatusCode 4 | { 5 | Ok = 200, 6 | Created = 201, 7 | Found = 302, 8 | SeeOther = 303, 9 | BadRequest = 400, 10 | Unauthorized = 401, 11 | Forbidden = 403, 12 | NotFound = 404, 13 | InternalServerError = 500 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Common/Models/View/AllReportViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace TORSHIA.App.Models.View 2 | { 3 | public class AllReportViewModel 4 | { 5 | public string Id { get; set; } 6 | 7 | public int Index { get; set; } 8 | 9 | public string Title { get; set; } 10 | 11 | public int Level { get; set; } 12 | 13 | public string Status { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Services/Contracts/IReportsService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using TORSHIA.Domain; 3 | 4 | namespace TORSHIA.Services.Contracts 5 | { 6 | public interface IReportsService 7 | { 8 | void CreateReport(string taskId, string reporterUsername); 9 | 10 | ICollection GetAllReports(); 11 | 12 | Report GetReportById(string reportId); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/SIS.Framework/ActionResults/Implementations/ViewResult.cs: -------------------------------------------------------------------------------- 1 | namespace SIS.Framework.ActionResults.Implementations 2 | { 3 | public class ViewResult : IViewable 4 | { 5 | public ViewResult(IRenderable view) 6 | { 7 | this.View = view; 8 | } 9 | 10 | public IRenderable View { get; set; } 11 | 12 | public string Invoke() => this.View.Render(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/SIS.Framework/Security/IIdentity.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace SIS.Framework.Security 4 | { 5 | public interface IIdentity 6 | { 7 | string Username { get; set; } 8 | 9 | string Password { get; set; } 10 | 11 | string Email { get; set; } 12 | 13 | bool IsValid { get; set; } 14 | 15 | IEnumerable Roles { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/SIS.HTTP/Exceptions/BadRequestException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SIS.HTTP.Exceptions 4 | { 5 | public class BadRequestException : Exception 6 | { 7 | private const string BadRequestMessage = "The Request is malformed."; 8 | 9 | public BadRequestException() : this(BadRequestMessage) { } 10 | 11 | public BadRequestException(string message) : base(message) { } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/SIS.Framework/Attributes/Method/HttpGetAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace SIS.Framework.Attributes.Method 2 | { 3 | public class HttpGetAttribute : HttpMethodAttribute 4 | { 5 | private const string HttpGetAttributeRequestMethod = "GET"; 6 | 7 | public override bool IsValid(string requestMethod) 8 | { 9 | return this.IsValidMethod(requestMethod, HttpGetAttributeRequestMethod); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/SIS.Framework/Attributes/Method/HttpPostAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace SIS.Framework.Attributes.Method 2 | { 3 | public class HttpPostAttribute : HttpMethodAttribute 4 | { 5 | private const string HttpPostAttributeRequestMethod = "POST"; 6 | 7 | public override bool IsValid(string requestMethod) 8 | { 9 | return this.IsValidMethod(requestMethod, HttpPostAttributeRequestMethod); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/SIS.Framework/Attributes/Method/HttpPutAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace SIS.Framework.Attributes.Method 2 | { 3 | public class HttpPutAttribute : HttpMethodAttribute 4 | { 5 | private const string HttpPutAttributeRequestMethod = "PUT"; 6 | 7 | public override bool IsValid(string requestMethod) 8 | { 9 | return this.IsValidMethod(requestMethod, HttpPutAttributeRequestMethod); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/SIS.Framework/ActionResults/Implementations/RedirectResult.cs: -------------------------------------------------------------------------------- 1 | namespace SIS.Framework.ActionResults.Implementations 2 | { 3 | public class RedirectResult : IRedirectable 4 | { 5 | public RedirectResult(string redirectUrl) 6 | { 7 | this.RedirectUrl = redirectUrl; 8 | } 9 | 10 | public string RedirectUrl { get; } 11 | 12 | public string Invoke() => this.RedirectUrl; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/SIS.HTTP/Cookies/IHttpCookieCollection.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | 5 | namespace SIS.HTTP.Cookies 6 | { 7 | public interface IHttpCookieCollection : IEnumerable 8 | { 9 | void Add(HttpCookie cookie); 10 | 11 | bool ContainsCookie(string key); 12 | 13 | HttpCookie GetCookie(string key); 14 | 15 | bool HasCookies(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/SIS.Framework/Attributes/Method/HttpDeleteAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace SIS.Framework.Attributes.Method 2 | { 3 | public class HttpDeleteAttribute : HttpMethodAttribute 4 | { 5 | private const string HttpDeleteAttributeRequestMethod = "DELETE"; 6 | 7 | public override bool IsValid(string requestMethod) 8 | { 9 | return this.IsValidMethod(requestMethod, HttpDeleteAttributeRequestMethod); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/SIS.Framework/Attributes/Method/HttpMethodAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SIS.Framework.Attributes.Method 4 | { 5 | public abstract class HttpMethodAttribute : Attribute 6 | { 7 | protected virtual bool IsValidMethod(string requestMethod, string attributeName) 8 | { 9 | return requestMethod.ToUpper() == attributeName; 10 | } 11 | 12 | public abstract bool IsValid(string requestMethod); 13 | } 14 | } -------------------------------------------------------------------------------- /src/SIS.Framework/SIS.Framework.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/SIS.WebServer/Results/RedirectResult.cs: -------------------------------------------------------------------------------- 1 | using SIS.HTTP.Enums; 2 | using SIS.HTTP.Headers; 3 | using SIS.HTTP.Responses; 4 | 5 | namespace SIS.WebServer.Results 6 | { 7 | public class RedirectResult : HttpResponse 8 | { 9 | public RedirectResult(string location) 10 | : base(HttpResponseStatusCode.SeeOther) 11 | { 12 | this.Headers.Add(new HttpHeader(HttpHeader.Location, location)); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Domain/User.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | using SIS.Framework.Security; 4 | 5 | namespace TORSHIA.Domain 6 | { 7 | public class User : IdentityUser 8 | { 9 | public string RoleId { get; set; } 10 | 11 | public UserRole Role { get; set; } 12 | 13 | [NotMapped] 14 | public override IEnumerable Roles => new[] {this.Role.Name}; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Common/Models/View/DetailsTaskViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace TORSHIA.App.Models.View 2 | { 3 | public class DetailsTaskViewModel 4 | { 5 | public string Title { get; set; } 6 | 7 | public string Description { get; set; } 8 | 9 | public int Level { get; set; } 10 | 11 | public string DueDate { get; set; } 12 | 13 | public string Participants { get; set; } 14 | 15 | public string AffectedSectors { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Common/Models/View/IndexTasksRowViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace TORSHIA.App.Models.View 4 | { 5 | public class IndexTasksRowViewModel 6 | { 7 | public IndexTasksRowViewModel() 8 | { 9 | this.Tasks = new List(); 10 | } 11 | 12 | public List Tasks { get; set; } 13 | 14 | public string[] Empty => new string[5 - this.Tasks.Count]; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/SIS.HTTP/Exceptions/InternalServerErrorException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SIS.HTTP.Exceptions 4 | { 5 | public class InternalServerErrorException : Exception 6 | { 7 | private const string InternalServerErrorExceptionMessage = "The Server has encountered an error."; 8 | 9 | public InternalServerErrorException() : this(InternalServerErrorExceptionMessage) { } 10 | 11 | public InternalServerErrorException(string message) : base(message) { } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Data/EntityConfiguration/TaskEntityConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | using TORSHIA.Domain; 4 | 5 | namespace TORSHIA.Data.EntityConfiguration 6 | { 7 | public class TaskEntityConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder builder) 10 | { 11 | builder 12 | .HasKey(t => t.Id); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/SIS.Framework/Views/View.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using SIS.Framework.ActionResults; 5 | 6 | namespace SIS.Framework.Views 7 | { 8 | public class View : IRenderable 9 | { 10 | private readonly string fullHtmlContent; 11 | 12 | public View(string fullHtmlContent) 13 | { 14 | this.fullHtmlContent = fullHtmlContent; 15 | } 16 | 17 | public string Render() => this.fullHtmlContent; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Data/EntityConfiguration/SectorEntityConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | using TORSHIA.Domain; 4 | 5 | namespace TORSHIA.Data.EntityConfiguration 6 | { 7 | public class SectorEntityConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder builder) 10 | { 11 | builder 12 | .HasKey(s => s.Id); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/SIS.WebServer/Results/FileResult.cs: -------------------------------------------------------------------------------- 1 | using SIS.HTTP.Headers; 2 | using SIS.HTTP.Responses; 3 | 4 | namespace SIS.WebServer.Results 5 | { 6 | public class FileResult : HttpResponse 7 | { 8 | public FileResult(byte[] content) 9 | { 10 | this.Headers.Add(new HttpHeader(HttpHeader.ContentLength, content.Length.ToString())); 11 | this.Headers.Add(new HttpHeader(HttpHeader.ContentDisposition, "inline")); 12 | this.Content = content; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Common/Models/Binding/CreateTaskBindingModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace TORSHIA.App.Models.Binding 5 | { 6 | public class CreateTaskBindingModel 7 | { 8 | public string Title { get; set; } 9 | 10 | public string Description { get; set; } 11 | 12 | public DateTime DueDate { get; set; } 13 | 14 | public string Participants { get; set; } 15 | 16 | public List AffectedSectors { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/SIS.Framework/Models/ViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace SIS.Framework.Models 4 | { 5 | public class ViewModel 6 | { 7 | public ViewModel() 8 | { 9 | this.Data = new Dictionary(); 10 | } 11 | 12 | public IDictionary Data { get; } 13 | 14 | public object this[string key] 15 | { 16 | get => this.Data[key]; 17 | set => this.Data[key] = value; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.App/Views/Shared/DisplayTemplates/AllReportViewModelDisplayTemplate.html: -------------------------------------------------------------------------------- 1 | 2 | @Model.Index 3 | @Model.Title 4 | @Model.Level 5 | @Model.Status 6 | 7 |
8 | Details 9 |
10 | 11 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Data/EntityConfiguration/UserRoleEntityConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | using TORSHIA.Domain; 4 | 5 | namespace TORSHIA.Data.EntityConfiguration 6 | { 7 | public class UserRoleEntityConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder builder) 10 | { 11 | builder 12 | .HasKey(ur => ur.Id); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Services/Contracts/ITasksService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using TORSHIA.App.Models.Binding; 3 | using TORSHIA.Domain; 4 | 5 | namespace TORSHIA.Services.Contracts 6 | { 7 | public interface ITasksService 8 | { 9 | void CreateTask(CreateTaskBindingModel createTaskBindingModel); 10 | 11 | ICollection GetAllUnreportedTasks(); 12 | 13 | Task GetTaskById(string taskId); 14 | 15 | void ReportTask(string taskId, string reporterUsername); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Services/TORSHIA.Services.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.App/Resources/style.css: -------------------------------------------------------------------------------- 1 | .bg-torshia { 2 | background-color: #FF8C00; 3 | } 4 | 5 | .text-torshia { 6 | color: #FF8C00; 7 | } 8 | 9 | .nav-link-white { 10 | color: #FFFFFF; 11 | } 12 | 13 | .hr-2 { 14 | height: 2px; 15 | } 16 | 17 | a { 18 | text-underline: none; 19 | } 20 | 21 | a:hover { 22 | outline: none; 23 | text-decoration: none; 24 | } 25 | 26 | textarea { 27 | resize: none; 28 | } 29 | 30 | footer { 31 | position: fixed; 32 | bottom: 0; 33 | border: 0; 34 | width: 98%; 35 | } -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Data/EntityConfiguration/ReportStatusEntityConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | using TORSHIA.Domain; 4 | 5 | namespace TORSHIA.Data.EntityConfiguration 6 | { 7 | public class ReportStatusEntityConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder builder) 10 | { 11 | builder 12 | .HasKey(rs => rs.Id); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/SIS.HTTP/Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace SIS.HTTP.Extensions 2 | { 3 | public static class StringExtensions 4 | { 5 | public static string Capitalize(this string text) 6 | { 7 | if (string.IsNullOrEmpty(text)) 8 | { 9 | return text; 10 | } 11 | 12 | if (text.Length == 1) 13 | { 14 | return text.ToUpper(); 15 | } 16 | 17 | return char.ToUpper(text[0]) + text.Substring(1).ToLower(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Domain/Report.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TORSHIA.Domain 4 | { 5 | public class Report 6 | { 7 | public string Id { get; set; } 8 | 9 | public DateTime ReportedOn { get; set; } 10 | 11 | public string StatusId { get; set; } 12 | 13 | public ReportStatus Status { get; set; } 14 | 15 | public string TaskId { get; set; } 16 | 17 | public Task Task { get; set; } 18 | 19 | public string ReporterId { get; set; } 20 | 21 | public User Reporter { get; set; } 22 | } 23 | } -------------------------------------------------------------------------------- /src/SIS.HTTP/Sessions/HttpSessionStorage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | 3 | namespace SIS.HTTP.Sessions 4 | { 5 | public class HttpSessionStorage 6 | { 7 | public const string SessionCookieKey = "SIS_ID"; 8 | 9 | private static readonly ConcurrentDictionary sessions 10 | = new ConcurrentDictionary(); 11 | 12 | public static IHttpSession GetSession(string id) 13 | { 14 | return sessions.GetOrAdd(id, _ => new HttpSession(id)); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.App/Views/Reports/All.html: -------------------------------------------------------------------------------- 1 |

Task Reports

2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | @Model.Collection.Reports(@Item) 15 | 16 |
#TaskLevelStatusActions
-------------------------------------------------------------------------------- /src/SIS.HTTP/Responses/IHttpResponse.cs: -------------------------------------------------------------------------------- 1 | using SIS.HTTP.Cookies; 2 | using SIS.HTTP.Enums; 3 | using SIS.HTTP.Headers; 4 | 5 | namespace SIS.HTTP.Responses 6 | { 7 | public interface IHttpResponse 8 | { 9 | HttpResponseStatusCode StatusCode { get; set; } 10 | 11 | IHttpHeaderCollection Headers { get; } 12 | 13 | IHttpCookieCollection Cookies { get; } 14 | 15 | byte[] Content { get; set; } 16 | 17 | void AddHeader(HttpHeader header); 18 | 19 | void AddCookie(HttpCookie cookie); 20 | 21 | byte[] GetBytes(); 22 | } 23 | } -------------------------------------------------------------------------------- /src/SIS.WebServer/Results/HtmlResult.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using SIS.HTTP.Enums; 3 | using SIS.HTTP.Headers; 4 | using SIS.HTTP.Responses; 5 | 6 | namespace SIS.WebServer.Results 7 | { 8 | public class HtmlResult : HttpResponse 9 | { 10 | public HtmlResult(string content, HttpResponseStatusCode responseStatusCode) 11 | : base(responseStatusCode) 12 | { 13 | this.Headers.Add(new HttpHeader(HttpHeader.ContentType, "text/html; charset=utf-8")); 14 | this.Content = Encoding.UTF8.GetBytes(content); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/SIS.WebServer/Results/TextResult.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using SIS.HTTP.Enums; 3 | using SIS.HTTP.Headers; 4 | using SIS.HTTP.Responses; 5 | 6 | namespace SIS.WebServer.Results 7 | { 8 | public class TextResult : HttpResponse 9 | { 10 | public TextResult(string content, HttpResponseStatusCode responseStatusCode) 11 | : base(responseStatusCode) 12 | { 13 | this.Headers.Add(new HttpHeader(HttpHeader.ContentType, "text/plain; charset=utf-8")); 14 | this.Content = Encoding.UTF8.GetBytes(content); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/SIS.Framework/Attributes/Property/RegexAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.Text.RegularExpressions; 3 | 4 | namespace SIS.Framework.Attributes.Property 5 | { 6 | public class RegexAttribute : ValidationAttribute 7 | { 8 | private readonly string pattern; 9 | 10 | public RegexAttribute(string pattern) 11 | { 12 | this.pattern = "^" + pattern + "$"; 13 | } 14 | 15 | public override bool IsValid(object value) 16 | { 17 | return Regex.IsMatch(value.ToString(), this.pattern); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Services/SectorService.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using TORSHIA.Data; 3 | using TORSHIA.Domain; 4 | using TORSHIA.Services.Contracts; 5 | 6 | namespace TORSHIA.Services 7 | { 8 | public class SectorsService : ISectorsService 9 | { 10 | private readonly TorshiaDbContext context; 11 | 12 | public SectorsService(TorshiaDbContext context) 13 | { 14 | this.context = context; 15 | } 16 | 17 | public Sector GetSectorById(string sectorId) 18 | => this.context 19 | .Sectors 20 | .SingleOrDefault(s => s.Id == sectorId); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Data/EntityConfiguration/UserEntityConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 6 | using TORSHIA.Domain; 7 | 8 | namespace TORSHIA.Data.EntityConfiguration 9 | { 10 | public class UserEntityConfiguration : IEntityTypeConfiguration 11 | { 12 | public void Configure(EntityTypeBuilder builder) 13 | { 14 | builder 15 | .HasKey(u => u.Id); 16 | 17 | builder 18 | .HasOne(u => u.Role); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.App/Views/Shared/DisplayTemplates/IndexTaskViewModelDisplayTemplate.html: -------------------------------------------------------------------------------- 1 |
2 |
@Model.Title
3 |
4 |
Level: @Model.Level
5 |
6 | 10 |
-------------------------------------------------------------------------------- /src/SIS.WebServer/Results/InlineResouceResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using SIS.HTTP.Enums; 4 | using SIS.HTTP.Headers; 5 | using SIS.HTTP.Responses; 6 | 7 | namespace SIS.WebServer.Results 8 | { 9 | public class InlineResouceResult : HttpResponse 10 | { 11 | public InlineResouceResult(byte[] content, HttpResponseStatusCode responseStatusCode) 12 | : base(responseStatusCode) 13 | { 14 | this.Headers.Add(new HttpHeader(HttpHeader.ContentLength, content.Length.ToString())); 15 | this.Headers.Add(new HttpHeader(HttpHeader.ContentDisposition, "inline")); 16 | this.Content = content; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Common/Models/View/DetailsReportViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace TORSHIA.App.Models.View 2 | { 3 | public class DetailsReportViewModel 4 | { 5 | public string Id { get; set; } 6 | 7 | public string Task { get; set; } 8 | 9 | public string Status { get; set; } 10 | 11 | public string DueDate { get; set; } 12 | 13 | public string ReportDate { get; set; } 14 | 15 | public string Reporter { get; set; } 16 | 17 | public string Participants { get; set; } 18 | 19 | public string AffectedSectors { get; set; } 20 | 21 | public string Description { get; set; } 22 | 23 | public int Level { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/SIS.HTTP/Requests/IHttpRequest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using SIS.HTTP.Cookies; 3 | using SIS.HTTP.Enums; 4 | using SIS.HTTP.Headers; 5 | using SIS.HTTP.Sessions; 6 | 7 | namespace SIS.HTTP.Requests 8 | { 9 | public interface IHttpRequest 10 | { 11 | string Path { get; } 12 | 13 | string Url { get; } 14 | 15 | Dictionary FormData { get; } 16 | 17 | Dictionary QueryData { get; } 18 | 19 | IHttpHeaderCollection Headers { get; } 20 | 21 | IHttpCookieCollection Cookies { get; } 22 | 23 | HttpRequestMethod RequestMethod { get; } 24 | 25 | IHttpSession Session { get; set; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/SIS.WebServer/Results/UnauthorizedResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using SIS.HTTP.Enums; 4 | using SIS.HTTP.Headers; 5 | using SIS.HTTP.Responses; 6 | 7 | namespace SIS.WebServer.Results 8 | { 9 | public class UnauthorizedResult : HttpResponse 10 | { 11 | private const string DefaultErrorHeading 12 | = "

You have no permission to access this functionality.

"; 13 | 14 | public UnauthorizedResult() 15 | : base(HttpResponseStatusCode.Unauthorized) 16 | { 17 | this.Headers.Add(new HttpHeader(HttpHeader.ContentType, "text/html")); 18 | this.Content = Encoding.UTF8.GetBytes(DefaultErrorHeading); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/SIS.Framework/MvcContext.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace SIS.Framework 4 | { 5 | public class MvcContext 6 | { 7 | private static MvcContext instance; 8 | 9 | private MvcContext() { } 10 | 11 | public static MvcContext Get => instance ?? (instance = new MvcContext()); 12 | 13 | public string AssemblyName { get; set; } = Assembly.GetEntryAssembly().GetName().Name; 14 | 15 | public string ControllerFolder { get; set; } = "Controllers"; 16 | 17 | public string ControllersSuffix { get; set; } = "Controller"; 18 | 19 | public string ViewsFolder { get; set; } = "Views"; 20 | 21 | public string ModelsFolder { get; set; } = "Models"; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/SIS.WebServer/Results/BadRequestResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using SIS.HTTP.Enums; 4 | using SIS.HTTP.Headers; 5 | using SIS.HTTP.Responses; 6 | 7 | namespace SIS.WebServer.Results 8 | { 9 | public class BadRequestResult : HttpResponse 10 | { 11 | private const string DefaultErrorHeading = "

Error of type occured, see details

"; 12 | 13 | public BadRequestResult(string content) 14 | : base(HttpResponseStatusCode.BadRequest) 15 | { 16 | content = DefaultErrorHeading + Environment.NewLine + content; 17 | this.Headers.Add(new HttpHeader(HttpHeader.ContentType, "text/html")); 18 | this.Content = Encoding.UTF8.GetBytes(content); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.App/TORSHIA.App.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/SIS.Framework/Security/IdentityUser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace SIS.Framework.Security 5 | { 6 | public class IdentityUser : IdentityUser 7 | { 8 | public IdentityUser() 9 | { 10 | this.Id = Guid.NewGuid().ToString(); 11 | } 12 | } 13 | 14 | public class IdentityUser : IIdentity where TKey : IEquatable 15 | { 16 | public virtual TKey Id { get; set; } 17 | 18 | public virtual string Username { get; set; } 19 | 20 | public virtual string Password { get; set; } 21 | 22 | public virtual string Email { get; set; } 23 | 24 | public virtual bool IsValid { get; set; } 25 | 26 | public virtual IEnumerable Roles { get; set; } 27 | } 28 | } -------------------------------------------------------------------------------- /src/SIS.WebServer/Results/InternalServerErrorResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using SIS.HTTP.Enums; 4 | using SIS.HTTP.Headers; 5 | using SIS.HTTP.Responses; 6 | 7 | namespace SIS.WebServer.Results 8 | { 9 | public class InternalServerErrorResult : HttpResponse 10 | { 11 | private const string DefaultErrorHeading = "

Internal Server Error occured, see details

"; 12 | 13 | public InternalServerErrorResult(string content) 14 | : base(HttpResponseStatusCode.InternalServerError) 15 | { 16 | content = DefaultErrorHeading + Environment.NewLine + content; 17 | this.Headers.Add(new HttpHeader(HttpHeader.ContentType, "text/html")); 18 | this.Content = Encoding.UTF8.GetBytes(content); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.App/Views/Users/Login.html: -------------------------------------------------------------------------------- 1 |

Login

2 |
3 |
4 |
5 | 6 | 7 |
8 |
9 | 10 | 11 |
12 |
13 | 14 |
15 |
-------------------------------------------------------------------------------- /src/SIS.Framework/Attributes/Property/NumberRangeAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace SIS.Framework.Attributes.Property 4 | { 5 | public class NumberRangeAttribute : ValidationAttribute 6 | { 7 | private readonly double minimumValue; 8 | 9 | private readonly double maximumValue; 10 | 11 | public NumberRangeAttribute( 12 | double minimumValue = double.MinValue, 13 | double maximumValue = double.MaxValue) 14 | { 15 | this.minimumValue = minimumValue; 16 | this.maximumValue = maximumValue; 17 | } 18 | 19 | public override bool IsValid(object value) 20 | { 21 | return this.minimumValue <= (double) value 22 | && this.maximumValue >= (double) value; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Data/TORSHIA.Data.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | all 13 | runtime; build; native; contentfiles; analyzers 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Data/EntityConfiguration/TaskSectorEntityConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | using TORSHIA.Domain; 4 | 5 | namespace TORSHIA.Data.EntityConfiguration 6 | { 7 | public class TaskSectorEntityConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder builder) 10 | { 11 | builder 12 | .HasKey(ts => new {ts.TaskId, ts.SectorId}); 13 | 14 | builder 15 | .HasOne(ts => ts.Task) 16 | .WithMany(t => t.AffectedSectors) 17 | .HasForeignKey(ts => ts.TaskId); 18 | 19 | builder 20 | .HasOne(ts => ts.Sector) 21 | .WithMany() 22 | .HasForeignKey(ts => ts.SectorId); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Domain/Task.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace TORSHIA.Domain 5 | { 6 | public class Task 7 | { 8 | public Task() 9 | { 10 | this.AffectedSectors = new List(); 11 | } 12 | 13 | public string Id { get; set; } 14 | 15 | public string Title { get; set; } 16 | 17 | public string Description { get; set; } 18 | 19 | public DateTime DueDate { get; set; } 20 | 21 | public bool IsReported { get; set; } 22 | 23 | /// 24 | /// By Description - this is stored however we want. I decided to store it in a simple String. 25 | /// An alternative to this may be just another simple entity - TaskParticipant 26 | /// 27 | public string ParticipantsString { get; set; } 28 | 29 | public ICollection AffectedSectors { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Data/EntityConfiguration/ReportEntityConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | using TORSHIA.Domain; 4 | 5 | namespace TORSHIA.Data.EntityConfiguration 6 | { 7 | public class ReportEntityConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder builder) 10 | { 11 | builder 12 | .HasKey(r => r.Id); 13 | 14 | builder 15 | .HasOne(r => r.Reporter) 16 | .WithMany() 17 | .HasForeignKey(r => r.ReporterId); 18 | 19 | builder 20 | .HasOne(r => r.Status) 21 | .WithMany() 22 | .HasForeignKey(r => r.StatusId); 23 | 24 | builder 25 | .HasOne(r => r.Task) 26 | .WithMany() 27 | .HasForeignKey(r => r.TaskId); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.App/Controllers/BaseController.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Runtime.CompilerServices; 3 | using SIS.Framework.ActionResults; 4 | using SIS.Framework.Controllers; 5 | 6 | namespace TORSHIA.App.Controllers 7 | { 8 | public abstract class BaseController : Controller 9 | { 10 | protected override IViewable View([CallerMemberName] string actionName = "") 11 | { 12 | this.Model["guestNavbarDisplay"] = "none"; 13 | this.Model["userNavbarDisplay"] = "none"; 14 | this.Model["adminNavbarDisplay"] = "none"; 15 | 16 | if (this.Identity == null) 17 | { 18 | this.Model["guestNavbarDisplay"] = "flex"; 19 | } else if (this.Identity != null && this.Identity.Roles.Contains("User")) 20 | { 21 | this.Model["userNavbarDisplay"] = "flex"; 22 | } else if (this.Identity != null && this.Identity.Roles.Contains("Admin")) 23 | { 24 | this.Model["adminNavbarDisplay"] = "flex"; 25 | } 26 | 27 | return base.View(actionName); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/SIS.Framework/WebHost.cs: -------------------------------------------------------------------------------- 1 | using SIS.Framework.Api; 2 | using SIS.Framework.Routers; 3 | using SIS.Framework.Routers.Contracts; 4 | using SIS.Framework.Services; 5 | using SIS.WebServer; 6 | using SIS.WebServer.Api; 7 | 8 | namespace SIS.Framework 9 | { 10 | public static class WebHost 11 | { 12 | private const int HostingPort = 8000; 13 | 14 | public static void Start(IMvcApplication application) 15 | { 16 | IDependencyContainer container = new DependencyContainer(); 17 | application.ConfigureServices(container); 18 | 19 | IMvcRouter controllerRouter = new ControllerRouter(container); 20 | IResourceRouter resourceRouter = new ResourceRouter(); 21 | ICustomRouter customRouter = new CustomRouter(); 22 | 23 | IHttpRequestHandler httpRequestHandlingContext 24 | = new HttpRequestHandlingContext(controllerRouter, resourceRouter, customRouter); 25 | 26 | application.Configure(); 27 | 28 | Server server = new Server(HostingPort, httpRequestHandlingContext); 29 | server.Run(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 SoftUni-CSharp-Web-Development 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/SIS.HTTP/Common/CoreValidator.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace SIS.HTTP.Common 4 | { 5 | using System; 6 | 7 | public class CoreValidator 8 | { 9 | public static void ThrowIfNull(object obj, string name) 10 | { 11 | if (obj == null) 12 | { 13 | throw new ArgumentNullException(name); 14 | } 15 | } 16 | 17 | public static void ThrowIfNullOrEmpty(string text, string name) 18 | { 19 | if (string.IsNullOrEmpty(text)) 20 | { 21 | throw new ArgumentException($"{name} cannot be null or empty.", name); 22 | } 23 | } 24 | 25 | public static void ThrowIfAllNull(params object[] arguments) 26 | { 27 | foreach (var argument in arguments) 28 | { 29 | if (argument != null) 30 | { 31 | return; 32 | } 33 | } 34 | 35 | throw new ArgumentException($"All arguments are null: {string.Join(", ", arguments)}."); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/SIS.HTTP/Headers/HttpHeader.cs: -------------------------------------------------------------------------------- 1 | namespace SIS.HTTP.Headers 2 | { 3 | using Common; 4 | 5 | public class HttpHeader 6 | { 7 | public const string Cookie = "Cookie"; 8 | 9 | public const string ContentType = "Content-Type"; 10 | 11 | public const string ContentLength = "Content-Length"; 12 | 13 | public const string ContentDisposition = "Content-Disposition"; 14 | 15 | public const string Authorization = "Authorization"; 16 | 17 | public const string Host = "Host"; 18 | 19 | public const string Server = "Server"; 20 | 21 | public const string Location = "Location"; 22 | 23 | public HttpHeader(string key, string value) 24 | { 25 | CoreValidator.ThrowIfNullOrEmpty(key, nameof(key)); 26 | CoreValidator.ThrowIfNullOrEmpty(value, nameof(value)); 27 | 28 | this.Key = key; 29 | this.Value = value; 30 | } 31 | 32 | public string Key { get; } 33 | 34 | public string Value { get; } 35 | 36 | public override string ToString() 37 | { 38 | return $"{this.Key}: {this.Value}"; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/SIS.HTTP/Headers/HttpHeaderCollection.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using SIS.HTTP.Common; 3 | 4 | namespace SIS.HTTP.Headers 5 | { 6 | public class HttpHeaderCollection : IHttpHeaderCollection 7 | { 8 | private readonly Dictionary headers; 9 | 10 | public HttpHeaderCollection() 11 | { 12 | this.headers = new Dictionary(); 13 | } 14 | 15 | public void Add(HttpHeader header) 16 | { 17 | CoreValidator.ThrowIfNull(header, nameof(header)); 18 | this.headers.Add(header.Key, header); 19 | } 20 | 21 | public bool ContainsHeader(string key) 22 | { 23 | CoreValidator.ThrowIfNull(key, nameof(key)); 24 | return this.headers.ContainsKey(key); 25 | } 26 | 27 | public HttpHeader GetHeader(string key) 28 | { 29 | CoreValidator.ThrowIfNull(key, nameof(key)); 30 | return this.headers.GetValueOrDefault(key, null); 31 | } 32 | 33 | public override string ToString() 34 | { 35 | return string.Join(GlobalConstants.HttpNewLine, this.headers.Values); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/SIS.HTTP/Extensions/HttpResponseStatusExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SIS.HTTP.Enums; 3 | 4 | namespace SIS.HTTP.Extensions 5 | { 6 | public static class HttpResponseStatusExtensions 7 | { 8 | private const string NotSupportedStatusCodeExceptionMessage = "Status Code {0} not supported."; 9 | 10 | private static string GetLineByCode(int code) 11 | { 12 | switch (code) 13 | { 14 | case 200: return "200 OK"; 15 | case 201: return "201 Created"; 16 | case 302: return "302 Found"; 17 | case 303: return "303 See Other"; 18 | case 400: return "400 Bad Request"; 19 | case 401: return "401 Unauthorized"; 20 | case 403: return "403 Forbidden"; 21 | case 404: return "404 Not Found"; 22 | case 500: return "500 Internal Server Error"; 23 | } 24 | 25 | throw new NotSupportedException(string.Format(NotSupportedStatusCodeExceptionMessage, code)); 26 | } 27 | 28 | public static string GetResponseLine(this HttpResponseStatusCode httpResponseStatus) 29 | { 30 | return GetLineByCode((int) httpResponseStatus); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.App/Views/Users/Register.html: -------------------------------------------------------------------------------- 1 |

Register

2 |
3 |
4 |
5 | 6 | 7 |
8 |
9 | 10 | 11 |
12 |
13 | 14 | 15 |
16 |
17 | 18 | 19 |
20 |
21 | 22 |
23 |
-------------------------------------------------------------------------------- /src/SIS.Framework/Attributes/Action/AuthorizeAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using SIS.Framework.Security; 4 | 5 | namespace SIS.Framework.Attributes.Action 6 | { 7 | public class AuthorizeAttribute : Attribute 8 | { 9 | private readonly string[] roles; 10 | 11 | private string[] FormatRoles(string[] inputRoles) 12 | { 13 | return inputRoles.Length > 0 14 | ? inputRoles.Select(r => r.ToLower()).ToArray() 15 | : inputRoles; 16 | } 17 | 18 | public AuthorizeAttribute() 19 | { 20 | this.roles = new string[0]; 21 | } 22 | 23 | public AuthorizeAttribute(params string[] roles) 24 | { 25 | this.roles = this.FormatRoles(roles); 26 | } 27 | 28 | private bool IsIdentityPresent(IIdentity identity) 29 | => identity != null; 30 | 31 | private bool IsIdentityInRole(IIdentity identity) 32 | => this.IsIdentityPresent(identity) 33 | && identity.Roles.Any(r => this.roles.Contains(r.ToLower())); 34 | 35 | public bool IsAuthorized(IIdentity user) 36 | => this.roles.Length > 0 37 | ? this.IsIdentityInRole(user) 38 | : this.IsIdentityPresent(user); 39 | } 40 | } -------------------------------------------------------------------------------- /src/Apps/TORSHIA.App/Resources/reset-css.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } -------------------------------------------------------------------------------- /src/Apps/TORSHIA.App/Views/Shared/DisplayTemplates/DetailsTaskViewModelDisplayTemplate.html: -------------------------------------------------------------------------------- 1 |
2 |

@Model.Title

3 |
4 |
5 |
6 |

Level: @Model.Level

7 |

Due Date: @Model.DueDate

8 |
9 |
10 |
11 |
12 |

Participants

13 |
14 |
15 | @Model.Participants 16 |
17 |
18 |
19 |
20 |

Affected Sectors

21 |
22 |
@Model.AffectedSectors
23 |
24 |
25 |
26 |
27 |

Description

28 |
29 |

30 | @Model.Description 31 |

32 |
-------------------------------------------------------------------------------- /src/SIS.HTTP/Sessions/HttpSession.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace SIS.HTTP.Sessions 4 | { 5 | using Common; 6 | 7 | public class HttpSession : IHttpSession 8 | { 9 | private readonly Dictionary sessionParameters; 10 | 11 | public HttpSession(string id) 12 | { 13 | CoreValidator.ThrowIfNull(id, nameof(id)); 14 | this.Id = id; 15 | this.sessionParameters = new Dictionary(); 16 | } 17 | 18 | public string Id { get; } 19 | 20 | public object GetParameter(string name) 21 | { 22 | CoreValidator.ThrowIfNullOrEmpty(name, nameof(name)); 23 | return this.sessionParameters.GetValueOrDefault(name, null); 24 | } 25 | 26 | public bool ContainsParameter(string name) 27 | { 28 | CoreValidator.ThrowIfNullOrEmpty(name, nameof(name)); 29 | return this.sessionParameters.ContainsKey(name); 30 | } 31 | 32 | public void AddParameter(string name, object parameter) 33 | { 34 | CoreValidator.ThrowIfNullOrEmpty(name, nameof(name)); 35 | CoreValidator.ThrowIfNull(parameter, nameof(parameter)); 36 | this.sessionParameters.Add(name, parameter); 37 | } 38 | 39 | public void ClearParameters() 40 | { 41 | this.sessionParameters.Clear(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Services/UsersService.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Microsoft.EntityFrameworkCore; 3 | using TORSHIA.App.Models.Binding; 4 | using TORSHIA.Data; 5 | using TORSHIA.Domain; 6 | using TORSHIA.Services.Contracts; 7 | 8 | namespace TORSHIA.Services 9 | { 10 | public class UsersService : IUsersService 11 | { 12 | private readonly TorshiaDbContext context; 13 | 14 | public UsersService(TorshiaDbContext context) 15 | { 16 | this.context = context; 17 | } 18 | 19 | public void CreateUser(RegisterUserBindingModel registerUserBindingModel) 20 | { 21 | User user = new User 22 | { 23 | Username = registerUserBindingModel.Username, 24 | Email = registerUserBindingModel.Email, 25 | Password = registerUserBindingModel.Password, 26 | Role = 27 | (this.context.Users.Any() 28 | ? this.context.UserRoles.SingleOrDefault(ur => ur.Name == "User") 29 | : this.context.UserRoles.SingleOrDefault(ur => ur.Name == "Admin")), 30 | IsValid = true 31 | }; 32 | 33 | this.context.Users.Add(user); 34 | this.context.SaveChanges(); 35 | } 36 | 37 | public User GetUserByUsername(string username) 38 | => this.context.Users 39 | .Include(u => u.Role) 40 | .SingleOrDefault(u => u.Username == username); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/SIS.Framework/HttpRequestHandlingContext.cs: -------------------------------------------------------------------------------- 1 | using SIS.Framework.Routers.Contracts; 2 | using SIS.HTTP.Enums; 3 | using SIS.HTTP.Requests; 4 | using SIS.HTTP.Responses; 5 | using SIS.WebServer.Api; 6 | 7 | namespace SIS.Framework 8 | { 9 | public class HttpRequestHandlingContext : IHttpRequestHandler 10 | { 11 | private readonly IMvcRouter mvcRouter; 12 | 13 | private readonly IResourceRouter resourceRouter; 14 | 15 | private readonly ICustomRouter customRouter; 16 | 17 | public HttpRequestHandlingContext(IMvcRouter mvcRouter, 18 | IResourceRouter resourceRouter, 19 | ICustomRouter customRouter) 20 | { 21 | this.mvcRouter = mvcRouter; 22 | this.resourceRouter = resourceRouter; 23 | this.customRouter = customRouter; 24 | } 25 | 26 | public IHttpResponse Handle(IHttpRequest httpRequest) 27 | { 28 | IHttpResponse response = null; 29 | 30 | if (this.resourceRouter.IsResourceRequest(httpRequest.Path)) 31 | { 32 | response = this.resourceRouter.Handle(httpRequest); 33 | } 34 | else if (this.customRouter.ContainsMapping(httpRequest)) 35 | { 36 | response = this.customRouter.Handle(httpRequest); 37 | } 38 | else 39 | { 40 | response = this.mvcRouter.Handle(httpRequest); 41 | } 42 | 43 | return response ?? new HttpResponse(HttpResponseStatusCode.NotFound); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/SIS.Framework/Routers/CustomRouter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using SIS.Framework.Routers.Contracts; 4 | using SIS.HTTP.Enums; 5 | using SIS.HTTP.Requests; 6 | using SIS.HTTP.Responses; 7 | 8 | namespace SIS.Framework.Routers 9 | { 10 | public class CustomRouter : ICustomRouter 11 | { 12 | public CustomRouter() 13 | { 14 | this.Routes = new Dictionary>> 15 | { 16 | [HttpRequestMethod.Get] = new Dictionary>(), 17 | [HttpRequestMethod.Post] = new Dictionary>(), 18 | [HttpRequestMethod.Put] = new Dictionary>(), 19 | [HttpRequestMethod.Delete] = new Dictionary>() 20 | }; 21 | } 22 | 23 | private Dictionary>> Routes { get; } 24 | 25 | public bool ContainsMapping(IHttpRequest httpRequest) 26 | => this.Routes.ContainsKey(httpRequest.RequestMethod) 27 | && this.Routes[httpRequest.RequestMethod].ContainsKey(httpRequest.Path.ToLower()); 28 | 29 | public IHttpResponse Handle(IHttpRequest httpRequest) 30 | { 31 | if (this.ContainsMapping(httpRequest)) 32 | { 33 | return this.Routes[httpRequest.RequestMethod][httpRequest.Path].Invoke(httpRequest); 34 | } 35 | 36 | return null; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Data/TorshiaDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using TORSHIA.Data.EntityConfiguration; 3 | using TORSHIA.Domain; 4 | 5 | namespace TORSHIA.Data 6 | { 7 | public class TorshiaDbContext : DbContext 8 | { 9 | public DbSet Users { get; set; } 10 | 11 | public DbSet UserRoles { get; set; } 12 | 13 | public DbSet Tasks { get; set; } 14 | 15 | public DbSet Sectors { get; set; } 16 | 17 | public DbSet TasksSectors { get; set; } 18 | 19 | public DbSet Reports { get; set; } 20 | 21 | public DbSet ReportStatuses { get; set; } 22 | 23 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 24 | { 25 | optionsBuilder 26 | .UseSqlServer("Server=.\\SQLEXPRESS;Database=TorshiaDB;Trusted_Connection=true;"); 27 | } 28 | 29 | protected override void OnModelCreating(ModelBuilder modelBuilder) 30 | { 31 | modelBuilder.ApplyConfiguration(new UserRoleEntityConfiguration()); 32 | modelBuilder.ApplyConfiguration(new ReportStatusEntityConfiguration()); 33 | modelBuilder.ApplyConfiguration(new SectorEntityConfiguration()); 34 | modelBuilder.ApplyConfiguration(new UserEntityConfiguration()); 35 | modelBuilder.ApplyConfiguration(new TaskEntityConfiguration()); 36 | modelBuilder.ApplyConfiguration(new ReportEntityConfiguration()); 37 | modelBuilder.ApplyConfiguration(new TaskSectorEntityConfiguration()); 38 | 39 | base.OnModelCreating(modelBuilder); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/SIS.WebServer/Server.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Sockets; 4 | using System.Threading.Tasks; 5 | using SIS.WebServer.Api; 6 | 7 | namespace SIS.WebServer 8 | { 9 | public class Server 10 | { 11 | private const string LocalhostIpAddress = "127.0.0.1"; 12 | 13 | private readonly int port; 14 | 15 | private readonly TcpListener listener; 16 | 17 | private readonly IHttpRequestHandler httpRequestHandler; 18 | 19 | private bool isRunning; 20 | 21 | private Server(int port) 22 | { 23 | this.port = port; 24 | this.listener = new TcpListener(IPAddress.Parse(LocalhostIpAddress), this.port); 25 | } 26 | 27 | public Server(int port, IHttpRequestHandler httpRequestHandler) : this(port) 28 | { 29 | this.httpRequestHandler = httpRequestHandler; 30 | } 31 | 32 | public void Run() 33 | { 34 | this.listener.Start(); 35 | this.isRunning = true; 36 | 37 | Console.WriteLine($"Server started at http://{LocalhostIpAddress}:{this.port}"); 38 | while (isRunning) 39 | { 40 | Console.WriteLine("Waiting for client..."); 41 | 42 | var client = listener.AcceptSocketAsync().GetAwaiter().GetResult(); 43 | 44 | Task.Run(() => Listen(client)); 45 | } 46 | } 47 | 48 | public async void Listen(Socket client) 49 | { 50 | ConnectionHandler connectionHandler = new ConnectionHandler(client, this.httpRequestHandler); 51 | await connectionHandler.ProcessRequestAsync(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.App/Views/Shared/DisplayTemplates/DetailsReportViewModelDisplayTemplate.html: -------------------------------------------------------------------------------- 1 |
2 |

Report - @Model.Id

3 |
4 |
5 |
6 |
7 |

Task: @Model.Task

8 |

Level: @Model.Level

9 |

Status: @Model.Status

10 |
11 |
12 |

Due Date: @Model.DueDate

13 |

Reported On: @Model.ReportDate

14 |

Reporter: @Model.Reporter

15 |
16 |
17 |
18 |
19 |
20 |

Participants

21 |
22 |
23 | @Model.Participants 24 |
25 |
26 |
27 |
28 |

Affected Sectors

29 |
30 |
@Model.AffectedSectors
31 |
32 |
33 |
34 |
35 |

Task Description

36 |
37 |

38 | @Model.Description 39 |

40 |
-------------------------------------------------------------------------------- /src/SIS.HTTP/Cookies/HttpCookieCollection.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Net; 3 | 4 | namespace SIS.HTTP.Cookies 5 | { 6 | using System.Collections.Generic; 7 | using Common; 8 | 9 | public class HttpCookieCollection : IHttpCookieCollection 10 | { 11 | private const string HttpCookieStringSeparator = "; "; 12 | 13 | private readonly Dictionary cookies; 14 | 15 | public HttpCookieCollection() 16 | { 17 | this.cookies = new Dictionary(); 18 | } 19 | 20 | public void Add(HttpCookie cookie) 21 | { 22 | CoreValidator.ThrowIfNull(cookie, nameof(cookie)); 23 | if (!this.ContainsCookie(cookie.Key)) 24 | { 25 | this.cookies.Add(cookie.Key, cookie); 26 | } 27 | } 28 | 29 | public bool ContainsCookie(string key) 30 | { 31 | CoreValidator.ThrowIfNull(key, nameof(key)); 32 | return this.cookies.ContainsKey(key); 33 | } 34 | 35 | public HttpCookie GetCookie(string key) 36 | { 37 | CoreValidator.ThrowIfNull(key, nameof(key)); 38 | return this.cookies.GetValueOrDefault(key, null); 39 | } 40 | 41 | public bool HasCookies() 42 | { 43 | return this.cookies.Count > 0; 44 | } 45 | 46 | public IEnumerator GetEnumerator() 47 | { 48 | foreach (var cookie in this.cookies) 49 | { 50 | yield return cookie.Value; 51 | } 52 | } 53 | 54 | public override string ToString() 55 | { 56 | return string.Join(HttpCookieStringSeparator, this.cookies.Values); 57 | } 58 | 59 | IEnumerator IEnumerable.GetEnumerator() 60 | { 61 | return GetEnumerator(); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/SIS.HTTP/Cookies/HttpCookie.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SIS.HTTP.Cookies 4 | { 5 | using Common; 6 | using System.Text; 7 | 8 | public class HttpCookie 9 | { 10 | private const int HttpCookieDefaultExpirationDays = 3; 11 | private const string HttpCookieDefaultPath = "/"; 12 | 13 | public HttpCookie(string key, string value, int expires = HttpCookieDefaultExpirationDays, string path = HttpCookieDefaultPath) 14 | { 15 | CoreValidator.ThrowIfNullOrEmpty(key, nameof(key)); 16 | CoreValidator.ThrowIfNullOrEmpty(value, nameof(value)); 17 | 18 | this.Key = key; 19 | this.Value = value; 20 | this.IsNew = true; 21 | this.Path = path; 22 | this.Expires = DateTime.UtcNow.AddDays(expires); 23 | } 24 | 25 | public HttpCookie(string key, string value, bool isNew, int expires = HttpCookieDefaultExpirationDays, string path = HttpCookieDefaultPath) 26 | : this(key, value, expires) 27 | { 28 | this.IsNew = isNew; 29 | } 30 | 31 | public string Key { get; } 32 | 33 | public string Value { get; } 34 | 35 | public DateTime Expires { get; private set; } 36 | 37 | public string Path { get; set; } 38 | 39 | public bool IsNew { get; } 40 | 41 | public bool HttpOnly { get; set; } = true; 42 | 43 | public void Delete() 44 | { 45 | this.Expires = DateTime.UtcNow.AddDays(-1); 46 | } 47 | 48 | public override string ToString() 49 | { 50 | var sb = new StringBuilder(); 51 | sb.Append($"{this.Key}={this.Value}; Expires={this.Expires:R}"); 52 | 53 | if (this.HttpOnly) 54 | { 55 | sb.Append("; HttpOnly"); 56 | } 57 | 58 | sb.Append($"; Path={this.Path}"); 59 | 60 | return sb.ToString(); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/SIS.Framework/Services/DependencyContainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace SIS.Framework.Services 7 | { 8 | public class DependencyContainer : IDependencyContainer 9 | { 10 | private readonly IDictionary dependencyDictionary; 11 | 12 | public DependencyContainer() 13 | { 14 | this.dependencyDictionary = new Dictionary(); 15 | } 16 | 17 | private Type this[Type key] 18 | => this.dependencyDictionary.ContainsKey(key) ? this.dependencyDictionary[key] : null; 19 | 20 | public void RegisterDependency() 21 | { 22 | this.dependencyDictionary[typeof(TSource)] = typeof(TDestination); 23 | } 24 | 25 | public T CreateInstance() 26 | => (T) this.CreateInstance(typeof(T)); 27 | 28 | public object CreateInstance(Type type) 29 | { 30 | if (type == null) return null; 31 | 32 | Type instanceType = this[type] ?? type; 33 | 34 | if (instanceType.IsInterface || instanceType.IsAbstract) 35 | { 36 | throw new InvalidOperationException($"Type {instanceType.FullName} cannot be instantiated."); 37 | } 38 | 39 | ConstructorInfo constructor = 40 | instanceType.GetConstructors().OrderBy(x => x.GetParameters().Length).First(); 41 | ParameterInfo[] constructorParameters = constructor.GetParameters(); 42 | object[] constructorParameterObjects = new object[constructorParameters.Length]; 43 | 44 | for (int i = 0; i < constructorParameters.Length; i++) 45 | { 46 | constructorParameterObjects[i] = this.CreateInstance(constructorParameters[i].ParameterType); 47 | } 48 | 49 | return constructor.Invoke(constructorParameterObjects); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Services/ReportsService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Microsoft.EntityFrameworkCore; 5 | using TORSHIA.Data; 6 | using TORSHIA.Domain; 7 | using TORSHIA.Services.Contracts; 8 | 9 | namespace TORSHIA.Services 10 | { 11 | public class ReportsService : IReportsService 12 | { 13 | private readonly TorshiaDbContext context; 14 | 15 | public ReportsService(TorshiaDbContext context) 16 | { 17 | this.context = context; 18 | } 19 | 20 | public void CreateReport(string taskId, string reporterUsername) 21 | { 22 | Report report = new Report 23 | { 24 | ReportedOn = DateTime.Now, 25 | Status = (new Random().Next(1, 5) == 4 26 | ? this.context.ReportStatuses.SingleOrDefault(rs => rs.Status == "Archived") 27 | : this.context.ReportStatuses.SingleOrDefault(rs => rs.Status == "Completed")), 28 | Task = this.context.Tasks.SingleOrDefault(t => t.Id == taskId), 29 | Reporter = this.context.Users.SingleOrDefault(u => u.Username == reporterUsername) 30 | }; 31 | 32 | this.context.Reports.Add(report); 33 | this.context.SaveChanges(); 34 | } 35 | 36 | public ICollection GetAllReports() 37 | => this.context 38 | .Reports 39 | .Include(r => r.Status) 40 | .Include(r => r.Task) 41 | .Include(r => r.Task.AffectedSectors) 42 | .Include(r => r.Reporter) 43 | .ToList(); 44 | 45 | public Report GetReportById(string reportId) 46 | => this.context 47 | .Reports 48 | .Include(r => r.Status) 49 | .Include(r => r.Task) 50 | .Include(r => r.Task.AffectedSectors) 51 | .Include(r => r.Reporter) 52 | .SingleOrDefault(r => r.Id == reportId); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/SIS.Framework/Controllers/Controller.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Runtime.CompilerServices; 3 | using SIS.Framework.ActionResults; 4 | using SIS.Framework.ActionResults.Implementations; 5 | using SIS.Framework.Models; 6 | using SIS.Framework.Security; 7 | using SIS.Framework.Utilities; 8 | using SIS.Framework.Views; 9 | using SIS.HTTP.Requests; 10 | 11 | namespace SIS.Framework.Controllers 12 | { 13 | public class Controller 14 | { 15 | private ViewEngine ViewEngine { get; } = new ViewEngine(); 16 | 17 | protected ViewModel Model { get; } = new ViewModel(); 18 | 19 | public IHttpRequest Request { get; set; } 20 | 21 | public IIdentity Identity 22 | => this.Request.Session.ContainsParameter("auth") 23 | ? (IIdentity)this.Request.Session.GetParameter("auth") 24 | : null; 25 | 26 | public Model ModelState { get; } = new Model(); 27 | 28 | protected virtual IViewable View([CallerMemberName] string actionName = "") 29 | { 30 | string controllerName = ControllerUtilities.GetControllerName(this); 31 | string viewContent = null; 32 | 33 | try 34 | { 35 | viewContent = this.ViewEngine.GetViewContent(controllerName, actionName); 36 | } 37 | catch (FileNotFoundException e) 38 | { 39 | this.Model.Data["Error"] = e.Message; 40 | 41 | viewContent = this.ViewEngine.GetErrorContent(); 42 | } 43 | 44 | string renderedContent = this.ViewEngine.RenderHtml(viewContent, this.Model.Data); 45 | return new ViewResult(new View(renderedContent)); 46 | } 47 | 48 | protected IRedirectable RedirectToAction(string redirectUrl) 49 | => new RedirectResult(redirectUrl); 50 | 51 | protected void SignIn(IIdentity auth) 52 | { 53 | this.Request.Session.AddParameter("auth", auth); 54 | } 55 | 56 | protected void SignOut() 57 | { 58 | this.Request.Session.ClearParameters(); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/Apps/TORSHIA.App/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using SIS.Framework.ActionResults; 4 | using TORSHIA.App.Models.View; 5 | using TORSHIA.Services.Contracts; 6 | 7 | namespace TORSHIA.App.Controllers 8 | { 9 | public class HomeController : BaseController 10 | { 11 | public readonly ITasksService tasksService; 12 | 13 | public HomeController(ITasksService tasksService) 14 | { 15 | this.tasksService = tasksService; 16 | } 17 | 18 | public IActionResult Index() 19 | { 20 | if (this.Identity != null) 21 | { 22 | this.Model["Username"] = this.Identity.Username; 23 | 24 | List taskViewModels = 25 | this.tasksService.GetAllUnreportedTasks() 26 | .Select(t => new IndexTaskViewModel 27 | { 28 | Id = t.Id, 29 | Title = t.Title, 30 | Level = t.AffectedSectors.Count 31 | }) 32 | .ToList(); 33 | 34 | List taskRowViewModels = new List(); 35 | 36 | for (int i = 0; i < taskViewModels.Count; i++) 37 | { 38 | if (i % 5 == 0) 39 | { 40 | taskRowViewModels.Add(new IndexTasksRowViewModel()); 41 | } 42 | 43 | taskRowViewModels[taskRowViewModels.Count - 1].Tasks.Add(taskViewModels[i]); 44 | } 45 | 46 | this.Model["TaskRows"] = taskRowViewModels; 47 | 48 | if (this.Identity.Roles.Contains("Admin")) 49 | { 50 | return this.View("Index-Admin"); 51 | } 52 | 53 | if (this.Identity.Roles.Contains("User")) 54 | { 55 | return this.View("Index-User"); 56 | } 57 | } 58 | 59 | return this.View(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.App/Controllers/UsersController.cs: -------------------------------------------------------------------------------- 1 | using SIS.Framework.ActionResults; 2 | using SIS.Framework.Attributes.Action; 3 | using SIS.Framework.Attributes.Method; 4 | using TORSHIA.App.Models.Binding; 5 | using TORSHIA.Domain; 6 | using TORSHIA.Services.Contracts; 7 | 8 | namespace TORSHIA.App.Controllers 9 | { 10 | public class UsersController : BaseController 11 | { 12 | public readonly IUsersService userService; 13 | 14 | public UsersController(IUsersService userService) 15 | { 16 | this.userService = userService; 17 | } 18 | 19 | [HttpGet] 20 | public IActionResult Login() 21 | { 22 | return this.View(); 23 | } 24 | 25 | [HttpPost] 26 | public IActionResult Login(LoginUserBindingModel bindingModel) 27 | { 28 | if (this.ModelState.IsValid != true) 29 | { 30 | return this.View(); 31 | } 32 | 33 | User userFromDb = this.userService.GetUserByUsername(bindingModel.Username); 34 | 35 | if (userFromDb == null || userFromDb.Password != bindingModel.Password) 36 | { 37 | return this.View(); 38 | } 39 | 40 | this.SignIn(userFromDb); 41 | return this.RedirectToAction("/Home/Index"); 42 | } 43 | 44 | [HttpGet] 45 | public IActionResult Register() 46 | { 47 | return this.View(); 48 | } 49 | 50 | [HttpPost] 51 | public IActionResult Register(RegisterUserBindingModel bindingModel) 52 | { 53 | if (this.ModelState.IsValid != true 54 | && bindingModel.Password != bindingModel.ConfirmPassword) 55 | { 56 | return this.View(); 57 | } 58 | 59 | this.userService.CreateUser(bindingModel); 60 | return this.RedirectToAction("/Users/Login"); 61 | } 62 | 63 | [HttpGet] 64 | [Authorize] 65 | public IActionResult Logout() 66 | { 67 | this.SignOut(); 68 | 69 | return this.RedirectToAction("/Home/Index"); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.App/StartUp.cs: -------------------------------------------------------------------------------- 1 | using SIS.Framework.Api; 2 | using SIS.Framework.Services; 3 | using TORSHIA.Data; 4 | using TORSHIA.Domain; 5 | using TORSHIA.Services; 6 | using TORSHIA.Services.Contracts; 7 | 8 | namespace TORSHIA.App 9 | { 10 | public class StartUp : MvcApplication 11 | { 12 | private void SeedDatabase() 13 | { 14 | using (var context = new TorshiaDbContext()) 15 | { 16 | context.Database.EnsureCreated(); 17 | 18 | context.UserRoles.Add(new UserRole { Name = "User" }); 19 | context.UserRoles.Add(new UserRole { Name = "Admin" }); 20 | 21 | context.Sectors.Add(new Sector { Name = "Customers" }); 22 | context.Sectors.Add(new Sector { Name = "Marketing" }); 23 | context.Sectors.Add(new Sector { Name = "Finances" }); 24 | context.Sectors.Add(new Sector { Name = "Internal" }); 25 | context.Sectors.Add(new Sector { Name = "Management" }); 26 | 27 | context.ReportStatuses.Add(new ReportStatus { Status = "Completed" }); 28 | context.ReportStatuses.Add(new ReportStatus { Status = "Archived" }); 29 | 30 | context.SaveChanges(); 31 | } 32 | } 33 | 34 | public override void Configure() 35 | { 36 | //using (var context = new TorshiaDbContext()) 37 | //{ 38 | // context.Database.Migrate(); 39 | //} 40 | 41 | //this.SeedDatabase(); 42 | } 43 | 44 | public override void ConfigureServices(IDependencyContainer dependencyContainer) 45 | { 46 | dependencyContainer.RegisterDependency(); 47 | 48 | dependencyContainer.RegisterDependency(); 49 | dependencyContainer.RegisterDependency(); 50 | dependencyContainer.RegisterDependency(); 51 | dependencyContainer.RegisterDependency(); 52 | 53 | base.ConfigureServices(dependencyContainer); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/SIS.Framework/Routers/ResourceRouter.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Linq; 3 | using SIS.Framework.Routers.Contracts; 4 | using SIS.HTTP.Enums; 5 | using SIS.HTTP.Requests; 6 | using SIS.HTTP.Responses; 7 | using SIS.WebServer.Results; 8 | 9 | namespace SIS.Framework.Routers 10 | { 11 | public class ResourceRouter : IResourceRouter 12 | { 13 | private const string RootDirectoryRelativePath = "../../../"; 14 | 15 | private const string ResourceFolderPath = "Resources/"; 16 | 17 | private static readonly string[] AllowedResourceExtensions = {".js", ".css", ".ico", ".jpg", ".jpeg", ".png", ".gif", ".html"}; 18 | 19 | private string FormatResourcePath(string httpRequestPath) 20 | { 21 | var indexOfStartOfExtension = httpRequestPath.LastIndexOf('.'); 22 | var indexOfStartOfNameOfResource = httpRequestPath.LastIndexOf('/'); 23 | 24 | var resourceName = httpRequestPath 25 | .Substring( 26 | indexOfStartOfNameOfResource); 27 | 28 | return RootDirectoryRelativePath 29 | + ResourceFolderPath 30 | + resourceName; 31 | } 32 | 33 | private bool IsAllowedExtension(string httpRequestPath) 34 | { 35 | var requestPathExtension = httpRequestPath 36 | .Substring(httpRequestPath.LastIndexOf('.')); 37 | 38 | return AllowedResourceExtensions.Contains(requestPathExtension); 39 | } 40 | 41 | public bool IsResourceRequest(string httpRequestPath) => httpRequestPath.Contains('.'); 42 | 43 | public IHttpResponse Handle(IHttpRequest httpRequest) 44 | { 45 | if (this.IsAllowedExtension(httpRequest.Path)) 46 | { 47 | string httpRequestPath = httpRequest.Path; 48 | 49 | string resourcePath = this.FormatResourcePath(httpRequestPath); 50 | 51 | if (!File.Exists(resourcePath)) 52 | { 53 | return null; 54 | } 55 | 56 | var fileContent = File.ReadAllBytes(resourcePath); 57 | 58 | return new InlineResouceResult(fileContent, HttpResponseStatusCode.Ok); 59 | } 60 | 61 | return null; 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /src/SIS.HTTP/Responses/HttpResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text; 3 | using SIS.HTTP.Common; 4 | using SIS.HTTP.Cookies; 5 | using SIS.HTTP.Enums; 6 | using SIS.HTTP.Extensions; 7 | using SIS.HTTP.Headers; 8 | 9 | namespace SIS.HTTP.Responses 10 | { 11 | public class HttpResponse : IHttpResponse 12 | { 13 | public HttpResponse() { } 14 | 15 | public HttpResponse(HttpResponseStatusCode statusCode) 16 | { 17 | CoreValidator.ThrowIfNull(statusCode, nameof(statusCode)); 18 | 19 | this.Headers = new HttpHeaderCollection(); 20 | this.Cookies = new HttpCookieCollection(); 21 | this.Content = new byte[0]; 22 | this.StatusCode = statusCode; 23 | } 24 | 25 | public HttpResponseStatusCode StatusCode { get; set; } 26 | 27 | public IHttpHeaderCollection Headers { get; } 28 | 29 | public IHttpCookieCollection Cookies { get; } 30 | 31 | public byte[] Content { get; set; } 32 | 33 | public void AddHeader(HttpHeader header) 34 | { 35 | CoreValidator.ThrowIfNull(header, nameof(header)); 36 | this.Headers.Add(header); 37 | } 38 | 39 | public void AddCookie(HttpCookie cookie) 40 | { 41 | CoreValidator.ThrowIfNull(cookie, nameof(cookie)); 42 | this.Cookies.Add(cookie); 43 | } 44 | 45 | public byte[] GetBytes() 46 | { 47 | return Encoding.UTF8.GetBytes(this.ToString()).Concat(this.Content).ToArray(); 48 | } 49 | 50 | public override string ToString() 51 | { 52 | StringBuilder result = new StringBuilder(); 53 | 54 | result 55 | .Append($"{GlobalConstants.HttpOneProtocolFragment} {this.StatusCode.GetResponseLine()}").Append(GlobalConstants.HttpNewLine) 56 | .Append(this.Headers).Append(GlobalConstants.HttpNewLine); 57 | 58 | if (this.Cookies.HasCookies()) 59 | { 60 | foreach (var httpCookie in this.Cookies) 61 | { 62 | result.Append($"Set-Cookie: {httpCookie}").Append(GlobalConstants.HttpNewLine); 63 | } 64 | } 65 | 66 | result.Append(GlobalConstants.HttpNewLine); 67 | 68 | return result.ToString(); 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /src/Apps/TORSHIA.Services/TasksService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Microsoft.EntityFrameworkCore; 5 | using TORSHIA.App.Models.Binding; 6 | using TORSHIA.Data; 7 | using TORSHIA.Domain; 8 | using TORSHIA.Services.Contracts; 9 | 10 | namespace TORSHIA.Services 11 | { 12 | public class TasksService : ITasksService 13 | { 14 | private readonly TorshiaDbContext context; 15 | 16 | private readonly IReportsService reportsService; 17 | 18 | public TasksService(TorshiaDbContext context, IReportsService reportsService) 19 | { 20 | this.context = context; 21 | this.reportsService = reportsService; 22 | } 23 | 24 | public void CreateTask(CreateTaskBindingModel createTaskBindingModel) 25 | { 26 | Task task = new Task 27 | { 28 | Title = createTaskBindingModel.Title, 29 | Description = createTaskBindingModel.Description, 30 | DueDate = createTaskBindingModel.DueDate, 31 | ParticipantsString = createTaskBindingModel.Participants, 32 | IsReported = false, 33 | }; 34 | 35 | if (createTaskBindingModel.AffectedSectors != null) 36 | { 37 | foreach (var affectedSector in createTaskBindingModel.AffectedSectors) 38 | { 39 | task.AffectedSectors.Add(new TaskSector 40 | { 41 | Sector = this.context.Sectors.SingleOrDefault(s => s.Name == affectedSector), 42 | Task = task 43 | }); 44 | } 45 | } 46 | 47 | this.context.Tasks.Add(task); 48 | this.context.SaveChanges(); 49 | } 50 | 51 | public ICollection GetAllUnreportedTasks() 52 | => this.context 53 | .Tasks 54 | .Include(t => t.AffectedSectors) 55 | .Where(t => t.IsReported == false) 56 | .ToList(); 57 | 58 | public Task GetTaskById(string taskId) 59 | => this.context.Tasks 60 | .Include(t => t.AffectedSectors) 61 | .SingleOrDefault(t => t.Id == taskId); 62 | 63 | public void ReportTask(string taskId, string reporterUsername) 64 | { 65 | Task task = this.GetTaskById(taskId); 66 | 67 | if (task == null) 68 | { 69 | return; 70 | } 71 | 72 | task.IsReported = true; 73 | 74 | this.reportsService.CreateReport(task.Id, reporterUsername); 75 | context.SaveChanges(); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.App/Controllers/ReportsController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Globalization; 3 | using System.Linq; 4 | using SIS.Framework.ActionResults; 5 | using SIS.Framework.Attributes.Action; 6 | using TORSHIA.App.Models.View; 7 | using TORSHIA.Domain; 8 | using TORSHIA.Services.Contracts; 9 | 10 | namespace TORSHIA.App.Controllers 11 | { 12 | public class ReportsController : BaseController 13 | { 14 | private readonly IReportsService reportsService; 15 | 16 | private readonly ISectorsService sectorsService; 17 | 18 | public ReportsController(IReportsService reportsService, ISectorsService sectorsService) 19 | { 20 | this.reportsService = reportsService; 21 | this.sectorsService = sectorsService; 22 | } 23 | 24 | [Authorize("Admin")] 25 | public IActionResult All() 26 | { 27 | List reportViewModels = 28 | this.reportsService.GetAllReports() 29 | .Select(r => new AllReportViewModel 30 | { 31 | Id = r.Id, 32 | Title = r.Task.Title, 33 | Level = r.Task.AffectedSectors.Count, 34 | Status = r.Status.Status 35 | }) 36 | .ToList(); 37 | 38 | for (int i = 0; i < reportViewModels.Count; i++) 39 | { 40 | reportViewModels[i].Index = i + 1; 41 | } 42 | 43 | this.Model["Reports"] = reportViewModels; 44 | return this.View(); 45 | } 46 | 47 | public IActionResult Details() 48 | { 49 | string reportId = this.Request.QueryData["id"].ToString(); 50 | 51 | Report report = this.reportsService.GetReportById(reportId); 52 | 53 | if (report == null) 54 | { 55 | return RedirectToAction("/Reports/All"); 56 | } 57 | 58 | List affectedSectors = report.Task.AffectedSectors 59 | .Select(ts => this.sectorsService.GetSectorById(ts.SectorId).Name) 60 | .ToList(); 61 | 62 | DetailsReportViewModel reportViewModel = new DetailsReportViewModel 63 | { 64 | Id = report.Id, 65 | Description = report.Task.Description, 66 | DueDate = report.Task.DueDate.ToString("dd/MM/yyyy", CultureInfo.InvariantCulture), 67 | ReportDate = report.ReportedOn.ToString("dd/MM/yyyy", CultureInfo.InvariantCulture), 68 | AffectedSectors = string.Join(", ", affectedSectors), 69 | Participants = report.Task.ParticipantsString, 70 | Reporter = report.Reporter.Username, 71 | Status = report.Status.Status, 72 | Task = report.Task.Title, 73 | Level = report.Task.AffectedSectors.Count 74 | }; 75 | 76 | this.Model["Report"] = reportViewModel; 77 | 78 | return this.View(); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.App/Controllers/TasksController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using Microsoft.EntityFrameworkCore; 6 | using SIS.Framework.ActionResults; 7 | using SIS.Framework.Attributes.Action; 8 | using SIS.Framework.Attributes.Method; 9 | using TORSHIA.App.Models.Binding; 10 | using TORSHIA.App.Models.View; 11 | using TORSHIA.Data; 12 | using TORSHIA.Domain; 13 | using TORSHIA.Services.Contracts; 14 | 15 | namespace TORSHIA.App.Controllers 16 | { 17 | public class TasksController : BaseController 18 | { 19 | public readonly ITasksService tasksService; 20 | 21 | public readonly ISectorsService sectorsService; 22 | 23 | public TasksController(ITasksService tasksService, ISectorsService sectorsService) 24 | { 25 | this.tasksService = tasksService; 26 | this.sectorsService = sectorsService; 27 | } 28 | 29 | [HttpGet] 30 | [Authorize("Admin")] 31 | public IActionResult Create() 32 | { 33 | return this.View(); 34 | } 35 | 36 | [HttpPost] 37 | [Authorize("Admin")] 38 | public IActionResult Create(CreateTaskBindingModel bindingModel) 39 | { 40 | if (this.ModelState.IsValid != true) 41 | { 42 | return this.View(); 43 | } 44 | 45 | this.tasksService.CreateTask(bindingModel); 46 | return this.RedirectToAction("/Home/Index"); 47 | } 48 | 49 | [HttpGet] 50 | [Authorize("Admin", "User")] 51 | public IActionResult Details() 52 | { 53 | string taskId = this.Request.QueryData["id"].ToString(); 54 | 55 | Task task = this.tasksService.GetTaskById(taskId); 56 | 57 | if (task == null) 58 | { 59 | return this.RedirectToAction("/Home/Index"); 60 | } 61 | 62 | List affectedSectors = task.AffectedSectors 63 | .Select(ts => this.sectorsService.GetSectorById(ts.SectorId).Name) 64 | .ToList(); 65 | 66 | DetailsTaskViewModel taskViewModel = new DetailsTaskViewModel 67 | { 68 | Title = task.Title, 69 | Description = task.Description, 70 | Participants = task.ParticipantsString, 71 | Level = task.AffectedSectors.Count, 72 | DueDate = task.DueDate.ToString("dd/MM/yyyy", CultureInfo.InvariantCulture), 73 | AffectedSectors = string.Join(", ", affectedSectors) 74 | }; 75 | 76 | this.Model["Task"] = taskViewModel; 77 | return this.View(); 78 | } 79 | 80 | [HttpGet] 81 | [Authorize("Admin", "User")] 82 | public IActionResult Report() 83 | { 84 | string taskId = this.Request.QueryData["id"].ToString(); 85 | 86 | this.tasksService.ReportTask(taskId, this.Identity.Username); 87 | return this.RedirectToAction("/Home/Index"); 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /src/Apps/TORSHIA.App/Views/Tasks/Create.html: -------------------------------------------------------------------------------- 1 |

Create Task

2 |
3 |
4 |
5 |
6 | 7 | 8 |
9 |
10 | 11 | 12 |
13 |
14 |
15 | 16 | 18 |
19 |
20 |
21 |
22 | 23 |
24 | 25 |
26 |
27 |
Affected Sectors
28 |
29 |
30 |
31 | 32 | 33 |
34 |
35 | 36 | 37 |
38 |
39 | 40 | 41 |
42 |
43 | 44 | 45 |
46 |
47 | 48 | 49 |
50 |
51 |
52 |
53 | 54 |
55 |
-------------------------------------------------------------------------------- /src/SIS.WebServer/ConnectionHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Sockets; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | using SIS.HTTP.Common; 6 | using SIS.HTTP.Cookies; 7 | using SIS.HTTP.Exceptions; 8 | using SIS.HTTP.Headers; 9 | using SIS.HTTP.Requests; 10 | using SIS.HTTP.Responses; 11 | using SIS.HTTP.Sessions; 12 | using SIS.WebServer.Api; 13 | using SIS.WebServer.Results; 14 | 15 | namespace SIS.WebServer 16 | { 17 | public class ConnectionHandler 18 | { 19 | private readonly Socket client; 20 | 21 | private readonly IHttpRequestHandler httpRequestHandler; 22 | 23 | public ConnectionHandler( 24 | Socket client, 25 | IHttpRequestHandler httpRequestHandler) 26 | { 27 | CoreValidator.ThrowIfNull(client, nameof(client)); 28 | CoreValidator.ThrowIfNull(httpRequestHandler, nameof(httpRequestHandler)); 29 | 30 | this.client = client; 31 | this.httpRequestHandler = httpRequestHandler; 32 | } 33 | 34 | private async Task ReadRequest() 35 | { 36 | var result = new StringBuilder(); 37 | var data = new ArraySegment(new byte[1024]); 38 | 39 | while (true) 40 | { 41 | int numberOfBytesRead = await this.client.ReceiveAsync(data.Array, SocketFlags.None); 42 | 43 | if (numberOfBytesRead == 0) 44 | { 45 | break; 46 | } 47 | 48 | var bytesAsString = Encoding.UTF8.GetString(data.Array, 0, numberOfBytesRead); 49 | result.Append(bytesAsString); 50 | 51 | if (numberOfBytesRead < 1023) 52 | { 53 | break; 54 | } 55 | } 56 | 57 | if (result.Length == 0) 58 | { 59 | return null; 60 | } 61 | 62 | return new HttpRequest(result.ToString()); 63 | } 64 | 65 | private async Task PrepareResponse(IHttpResponse httpResponse) 66 | { 67 | httpResponse.AddHeader(new HttpHeader(HttpHeader.Server, "SIS/0.0.1")); 68 | 69 | byte[] byteSegments = httpResponse.GetBytes(); 70 | 71 | await this.client.SendAsync(byteSegments, SocketFlags.None); 72 | } 73 | 74 | private string SetRequestSession(IHttpRequest httpRequest) 75 | { 76 | string sessionId = null; 77 | 78 | if (httpRequest.Cookies.ContainsCookie(HttpSessionStorage.SessionCookieKey)) 79 | { 80 | var cookie = httpRequest.Cookies.GetCookie(HttpSessionStorage.SessionCookieKey); 81 | sessionId = cookie.Value; 82 | httpRequest.Session = HttpSessionStorage.GetSession(sessionId); 83 | } 84 | else 85 | { 86 | sessionId = Guid.NewGuid().ToString(); 87 | httpRequest.Session = HttpSessionStorage.GetSession(sessionId); 88 | } 89 | 90 | return sessionId; 91 | } 92 | 93 | private void SetResponseSession(IHttpResponse httpResponse, string sessionId) 94 | { 95 | if (sessionId != null) 96 | { 97 | httpResponse 98 | .AddCookie(new HttpCookie(HttpSessionStorage.SessionCookieKey 99 | , sessionId)); 100 | } 101 | } 102 | 103 | public async Task ProcessRequestAsync() 104 | { 105 | try 106 | { 107 | var httpRequest = await this.ReadRequest(); 108 | 109 | if (httpRequest != null) 110 | { 111 | string sessionId = this.SetRequestSession(httpRequest); 112 | 113 | var httpResponse = this.httpRequestHandler.Handle(httpRequest); 114 | 115 | this.SetResponseSession(httpResponse, sessionId); 116 | 117 | await this.PrepareResponse(httpResponse); 118 | } 119 | } 120 | catch (BadRequestException e) 121 | { 122 | await this.PrepareResponse(new BadRequestResult(e.Message)); 123 | } 124 | catch (Exception e) 125 | { 126 | await this.PrepareResponse(new InternalServerErrorResult(e.StackTrace)); 127 | } 128 | 129 | this.client.Shutdown(SocketShutdown.Both); 130 | } 131 | } 132 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opensdf 80 | *.sdf 81 | *.cachefile 82 | 83 | # Visual Studio profiler 84 | *.psess 85 | *.vsp 86 | *.vspx 87 | *.sap 88 | 89 | # TFS 2012 Local Workspace 90 | $tf/ 91 | 92 | # Guidance Automation Toolkit 93 | *.gpState 94 | 95 | # ReSharper is a .NET coding add-in 96 | _ReSharper*/ 97 | *.[Rr]e[Ss]harper 98 | *.DotSettings.user 99 | 100 | # JustCode is a .NET coding add-in 101 | .JustCode 102 | 103 | # TeamCity is a build add-in 104 | _TeamCity* 105 | 106 | # DotCover is a Code Coverage Tool 107 | *.dotCover 108 | 109 | # NCrunch 110 | _NCrunch_* 111 | .*crunch*.local.xml 112 | nCrunchTemp_* 113 | 114 | # MightyMoose 115 | *.mm.* 116 | AutoTest.Net/ 117 | 118 | # Web workbench (sass) 119 | .sass-cache/ 120 | 121 | # Installshield output folder 122 | [Ee]xpress/ 123 | 124 | # DocProject is a documentation generator add-in 125 | DocProject/buildhelp/ 126 | DocProject/Help/*.HxT 127 | DocProject/Help/*.HxC 128 | DocProject/Help/*.hhc 129 | DocProject/Help/*.hhk 130 | DocProject/Help/*.hhp 131 | DocProject/Help/Html2 132 | DocProject/Help/html 133 | 134 | # Click-Once directory 135 | publish/ 136 | 137 | # Publish Web Output 138 | *.[Pp]ublish.xml 139 | *.azurePubxml 140 | # TODO: Comment the next line if you want to checkin your web deploy settings 141 | # but database connection strings (with potential passwords) will be unencrypted 142 | *.pubxml 143 | *.publishproj 144 | 145 | # NuGet Packages 146 | *.nupkg 147 | # The packages folder can be ignored because of Package Restore 148 | **/packages/* 149 | # except build/, which is used as an MSBuild target. 150 | !**/packages/build/ 151 | # Uncomment if necessary however generally it will be regenerated when needed 152 | #!**/packages/repositories.config 153 | 154 | # Windows Azure Build Output 155 | csx/ 156 | *.build.csdef 157 | 158 | # Windows Store app package directory 159 | AppPackages/ 160 | 161 | # Visual Studio cache files 162 | # files ending in .cache can be ignored 163 | *.[Cc]ache 164 | # but keep track of directories ending in .cache 165 | !*.[Cc]ache/ 166 | 167 | # Others 168 | ClientBin/ 169 | [Ss]tyle[Cc]op.* 170 | ~$* 171 | *~ 172 | *.dbmdl 173 | *.dbproj.schemaview 174 | *.pfx 175 | *.publishsettings 176 | node_modules/ 177 | orleans.codegen.cs 178 | 179 | # RIA/Silverlight projects 180 | Generated_Code/ 181 | 182 | # Backup & report files from converting an old project file 183 | # to a newer Visual Studio version. Backup files are not needed, 184 | # because we have git ;-) 185 | _UpgradeReport_Files/ 186 | Backup*/ 187 | UpgradeLog*.XML 188 | UpgradeLog*.htm 189 | 190 | # SQL Server files 191 | *.mdf 192 | *.ldf 193 | 194 | # Business Intelligence projects 195 | *.rdl.data 196 | *.bim.layout 197 | *.bim_*.settings 198 | 199 | # Microsoft Fakes 200 | FakesAssemblies/ 201 | 202 | # Node.js Tools for Visual Studio 203 | .ntvs_analysis.dat 204 | 205 | # Visual Studio 6 build log 206 | *.plg 207 | 208 | # Visual Studio 6 workspace options file 209 | *.opt 210 | 211 | # Visual Studio LightSwitch build output 212 | **/*.HTMLClient/GeneratedArtifacts 213 | **/*.DesktopClient/GeneratedArtifacts 214 | **/*.DesktopClient/ModelManifest.xml 215 | **/*.Server/GeneratedArtifacts 216 | **/*.Server/ModelManifest.xml 217 | _Pvt_Extensions 218 | -------------------------------------------------------------------------------- /src/Apps/TORSHIA.App/Views/Shared/_Layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | TORSHIA 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 | 34 | 50 | 72 |
73 |
74 | @RenderBody() 75 |
76 |
77 |
78 |
79 | © CopyRight Sanity Web Design Studios 2018. All rights reserved. 80 |
81 |
82 |
83 |
84 | 85 | -------------------------------------------------------------------------------- /src/SIS.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28010.2003 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SIS.WebServer", "SIS.WebServer\SIS.WebServer.csproj", "{9069BFBB-817C-4FA4-A980-E20E9DD7A469}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SIS.HTTP", "SIS.HTTP\SIS.HTTP.csproj", "{9C8ADFD1-16AE-4365-A160-A1C6091A0509}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SIS.Framework", "SIS.Framework\SIS.Framework.csproj", "{035FAB55-6E4D-4C26-AB35-DBB6332CBE58}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Apps", "Apps", "{2010CD02-CEBA-4C99-A411-9F654D30539C}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TORSHIA.App", "Apps\TORSHIA.App\TORSHIA.App.csproj", "{7436C26D-2441-4F68-84C1-2FB0F39D99FA}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TORSHIA.Common", "Apps\TORSHIA.Common\TORSHIA.Common.csproj", "{D5516A8C-7B1A-4309-8021-8C46351EF0F4}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TORSHIA.Data", "Apps\TORSHIA.Data\TORSHIA.Data.csproj", "{08731B76-082C-40F7-B668-74F942A7A8A6}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TORSHIA.Domain", "Apps\TORSHIA.Domain\TORSHIA.Domain.csproj", "{51B4DE1F-0133-4161-83FC-E75057BBFB25}" 21 | EndProject 22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TORSHIA.Services", "Apps\TORSHIA.Services\TORSHIA.Services.csproj", "{E1A72E6D-F060-45B6-8AA1-4A5AA82B4A80}" 23 | EndProject 24 | Global 25 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 26 | Debug|Any CPU = Debug|Any CPU 27 | Release|Any CPU = Release|Any CPU 28 | EndGlobalSection 29 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 30 | {9069BFBB-817C-4FA4-A980-E20E9DD7A469}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {9069BFBB-817C-4FA4-A980-E20E9DD7A469}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {9069BFBB-817C-4FA4-A980-E20E9DD7A469}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {9069BFBB-817C-4FA4-A980-E20E9DD7A469}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {9C8ADFD1-16AE-4365-A160-A1C6091A0509}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {9C8ADFD1-16AE-4365-A160-A1C6091A0509}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {9C8ADFD1-16AE-4365-A160-A1C6091A0509}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {9C8ADFD1-16AE-4365-A160-A1C6091A0509}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {035FAB55-6E4D-4C26-AB35-DBB6332CBE58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {035FAB55-6E4D-4C26-AB35-DBB6332CBE58}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {035FAB55-6E4D-4C26-AB35-DBB6332CBE58}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {035FAB55-6E4D-4C26-AB35-DBB6332CBE58}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {7436C26D-2441-4F68-84C1-2FB0F39D99FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 43 | {7436C26D-2441-4F68-84C1-2FB0F39D99FA}.Debug|Any CPU.Build.0 = Debug|Any CPU 44 | {7436C26D-2441-4F68-84C1-2FB0F39D99FA}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {7436C26D-2441-4F68-84C1-2FB0F39D99FA}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {D5516A8C-7B1A-4309-8021-8C46351EF0F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {D5516A8C-7B1A-4309-8021-8C46351EF0F4}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {D5516A8C-7B1A-4309-8021-8C46351EF0F4}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 | {D5516A8C-7B1A-4309-8021-8C46351EF0F4}.Release|Any CPU.Build.0 = Release|Any CPU 50 | {08731B76-082C-40F7-B668-74F942A7A8A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 51 | {08731B76-082C-40F7-B668-74F942A7A8A6}.Debug|Any CPU.Build.0 = Debug|Any CPU 52 | {08731B76-082C-40F7-B668-74F942A7A8A6}.Release|Any CPU.ActiveCfg = Release|Any CPU 53 | {08731B76-082C-40F7-B668-74F942A7A8A6}.Release|Any CPU.Build.0 = Release|Any CPU 54 | {51B4DE1F-0133-4161-83FC-E75057BBFB25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 55 | {51B4DE1F-0133-4161-83FC-E75057BBFB25}.Debug|Any CPU.Build.0 = Debug|Any CPU 56 | {51B4DE1F-0133-4161-83FC-E75057BBFB25}.Release|Any CPU.ActiveCfg = Release|Any CPU 57 | {51B4DE1F-0133-4161-83FC-E75057BBFB25}.Release|Any CPU.Build.0 = Release|Any CPU 58 | {E1A72E6D-F060-45B6-8AA1-4A5AA82B4A80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 59 | {E1A72E6D-F060-45B6-8AA1-4A5AA82B4A80}.Debug|Any CPU.Build.0 = Debug|Any CPU 60 | {E1A72E6D-F060-45B6-8AA1-4A5AA82B4A80}.Release|Any CPU.ActiveCfg = Release|Any CPU 61 | {E1A72E6D-F060-45B6-8AA1-4A5AA82B4A80}.Release|Any CPU.Build.0 = Release|Any CPU 62 | EndGlobalSection 63 | GlobalSection(SolutionProperties) = preSolution 64 | HideSolutionNode = FALSE 65 | EndGlobalSection 66 | GlobalSection(NestedProjects) = preSolution 67 | {7436C26D-2441-4F68-84C1-2FB0F39D99FA} = {2010CD02-CEBA-4C99-A411-9F654D30539C} 68 | {D5516A8C-7B1A-4309-8021-8C46351EF0F4} = {2010CD02-CEBA-4C99-A411-9F654D30539C} 69 | {08731B76-082C-40F7-B668-74F942A7A8A6} = {2010CD02-CEBA-4C99-A411-9F654D30539C} 70 | {51B4DE1F-0133-4161-83FC-E75057BBFB25} = {2010CD02-CEBA-4C99-A411-9F654D30539C} 71 | {E1A72E6D-F060-45B6-8AA1-4A5AA82B4A80} = {2010CD02-CEBA-4C99-A411-9F654D30539C} 72 | EndGlobalSection 73 | GlobalSection(ExtensibilityGlobals) = postSolution 74 | SolutionGuid = {7DDABD09-DCFC-4DD2-996C-EDD241D1D1F0} 75 | EndGlobalSection 76 | EndGlobal 77 | -------------------------------------------------------------------------------- /src/SIS.Framework/Views/ViewEngine.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text.RegularExpressions; 6 | 7 | namespace SIS.Framework.Views 8 | { 9 | public class ViewEngine 10 | { 11 | private const string ViewPathPrefix = @"..\..\.."; 12 | 13 | private const string DisplayTemplateSuffix = "DisplayTemplate"; 14 | 15 | private const string LayoutViewName = "_Layout"; 16 | 17 | private const string ErrorViewName = "_Error"; 18 | 19 | private const string ViewExtension = "html"; 20 | 21 | private const string ModelCollectionViewParameterPattern = @"\@Model\.Collection\.(\w+)\((.+)\)"; 22 | 23 | private string ViewsFolderPath => $@"{ViewPathPrefix}\{MvcContext.Get.ViewsFolder}\"; 24 | 25 | private string ViewsSharedFolderPath => $@"{this.ViewsFolderPath}Shared\"; 26 | 27 | private string ViewsDisplayTemplateFolderPath => $@"{this.ViewsSharedFolderPath}\DisplayTemplates\"; 28 | 29 | private string FormatLayoutViewPath() 30 | => $@"{this.ViewsSharedFolderPath}{LayoutViewName}.{ViewExtension}"; 31 | 32 | private string FormatErrorViewPath() 33 | => $@"{this.ViewsSharedFolderPath}{ErrorViewName}.{ViewExtension}"; 34 | 35 | private string FormatViewPath(string controllerName, string actionName) 36 | => $@"{this.ViewsFolderPath}\{controllerName}\{actionName}.{ViewExtension}"; 37 | 38 | private string FormatDisplayTemplatePath(string objectName) 39 | => $@"{this.ViewsDisplayTemplateFolderPath}{objectName}{DisplayTemplateSuffix}.{ViewExtension}"; 40 | 41 | private string ReadLayoutHtml(string layoutViewPath) 42 | { 43 | if (!File.Exists(layoutViewPath)) 44 | { 45 | throw new FileNotFoundException($"Layout View does not exist."); 46 | } 47 | 48 | return File.ReadAllText(layoutViewPath); 49 | } 50 | 51 | private string ReadErrorHtml(string errorViewPath) 52 | { 53 | if (!File.Exists(errorViewPath)) 54 | { 55 | throw new FileNotFoundException($"Error View does not exist."); 56 | } 57 | 58 | return File.ReadAllText(errorViewPath); 59 | } 60 | 61 | private string ReadViewHtml(string viewPath) 62 | { 63 | if (!File.Exists(viewPath)) 64 | { 65 | throw new FileNotFoundException($"View does not exist at {viewPath}"); 66 | } 67 | 68 | return File.ReadAllText(viewPath); 69 | } 70 | 71 | private string RenderObject(object viewObject, string displayTemplate) 72 | { 73 | foreach (var property in viewObject.GetType().GetProperties()) 74 | { 75 | displayTemplate = 76 | this.RenderViewData(displayTemplate 77 | , property.GetValue(viewObject) 78 | , property.Name); 79 | } 80 | 81 | return displayTemplate; 82 | } 83 | 84 | private string RenderViewData(string template 85 | , object viewObject 86 | , string viewObjectName = null) 87 | { 88 | if (viewObject != null 89 | && viewObject.GetType() != typeof(string) 90 | && viewObject is IEnumerable enumerable 91 | && Regex.IsMatch(template, ModelCollectionViewParameterPattern)) 92 | { 93 | Match collectionMatch = 94 | Regex.Matches(template, ModelCollectionViewParameterPattern) 95 | .First(cm => cm.Groups[1].Value == viewObjectName); 96 | 97 | string fullMatch = collectionMatch.Groups[0].Value; 98 | string itemPattern = collectionMatch.Groups[2].Value; 99 | 100 | string result = string.Empty; 101 | 102 | foreach (var subObject in enumerable) 103 | { 104 | result += itemPattern 105 | .Replace("@Item", this.RenderViewData(template, subObject)); 106 | } 107 | 108 | return template.Replace(fullMatch, result); 109 | } 110 | 111 | if (viewObject != null 112 | && !viewObject.GetType().IsPrimitive 113 | && viewObject.GetType() != typeof(string)) 114 | { 115 | if (File.Exists(this.FormatDisplayTemplatePath(viewObject.GetType().Name))) 116 | { 117 | string renderedObject = this.RenderObject(viewObject, 118 | File.ReadAllText(this.FormatDisplayTemplatePath(viewObject.GetType().Name))); 119 | 120 | return viewObjectName != null 121 | ? template.Replace($"@Model.{viewObjectName}", renderedObject) 122 | : renderedObject; 123 | } 124 | } 125 | 126 | return viewObjectName != null 127 | ? template.Replace($"@Model.{viewObjectName}", viewObject?.ToString()) 128 | : viewObject?.ToString(); 129 | } 130 | 131 | public string GetErrorContent() 132 | => this.ReadLayoutHtml(this.FormatLayoutViewPath()) 133 | .Replace("@RenderError()", this.ReadErrorHtml(this.FormatErrorViewPath())); 134 | 135 | public string GetViewContent(string controllerName, string actionName) 136 | => this.ReadLayoutHtml(this.FormatLayoutViewPath()) 137 | .Replace("@RenderBody()", this.ReadViewHtml(this.FormatViewPath(controllerName, actionName))); 138 | 139 | public string RenderHtml(string fullHtmlContent, IDictionary viewData) 140 | { 141 | string renderedHtml = fullHtmlContent; 142 | 143 | if (viewData.Count > 0) 144 | { 145 | foreach (var parameter in viewData) 146 | { 147 | renderedHtml = 148 | this.RenderViewData(renderedHtml, parameter.Value, parameter.Key); 149 | } 150 | } 151 | 152 | if (viewData.ContainsKey("Error")) 153 | { 154 | renderedHtml = renderedHtml.Replace("@Error", viewData["Error"].ToString()); 155 | } 156 | 157 | return renderedHtml; 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/SIS.HTTP/Requests/HttpRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using SIS.HTTP.Common; 6 | using SIS.HTTP.Cookies; 7 | using SIS.HTTP.Enums; 8 | using SIS.HTTP.Exceptions; 9 | using SIS.HTTP.Extensions; 10 | using SIS.HTTP.Headers; 11 | using SIS.HTTP.Sessions; 12 | 13 | namespace SIS.HTTP.Requests 14 | { 15 | public class HttpRequest : IHttpRequest 16 | { 17 | private const char HttpRequestUrlQuerySeparator = '?'; 18 | 19 | private const char HttpRequestUrlFragmentSeparator = '#'; 20 | 21 | private const string HttpRequestHeaderNameValueSeparator = ": "; 22 | 23 | private const string HttpRequestCookiesSeparator = "; "; 24 | 25 | private const char HttpRequestCookieNameValueSeparator = '='; 26 | 27 | private const char HttpRequestParameterSeparator = '&'; 28 | 29 | private const char HttpRequestParameterNameValueSeparator = '='; 30 | 31 | public HttpRequest(string requestString) 32 | { 33 | CoreValidator.ThrowIfNullOrEmpty(requestString, nameof(requestString)); 34 | 35 | this.FormData = new Dictionary(); 36 | this.QueryData = new Dictionary(); 37 | this.Headers = new HttpHeaderCollection(); 38 | this.Cookies = new HttpCookieCollection(); 39 | 40 | this.ParseRequest(requestString); 41 | } 42 | 43 | public string Path { get; private set; } 44 | 45 | public string Url { get; private set; } 46 | 47 | public Dictionary FormData { get; } 48 | 49 | public Dictionary QueryData { get; } 50 | 51 | public IHttpHeaderCollection Headers { get; } 52 | 53 | public IHttpCookieCollection Cookies { get; } 54 | 55 | public HttpRequestMethod RequestMethod { get; private set; } 56 | 57 | public IHttpSession Session { get; set; } 58 | 59 | private bool IsValidRequestLine(string[] requestLine) 60 | { 61 | return requestLine.Length == 3 62 | && requestLine[2].ToLower() 63 | != GlobalConstants.HttpOneProtocolFragment; 64 | } 65 | 66 | private bool IsValidRequestQueryString(string queryString, string[] queryParameters) 67 | { 68 | return !(string.IsNullOrEmpty(queryString) || queryParameters.Length < 1); 69 | } 70 | 71 | private void ParseRequestMethod(string[] requestLine) 72 | { 73 | bool parseMethodResult = Enum.TryParse(requestLine[0].Capitalize(), out HttpRequestMethod parsedMethod); 74 | 75 | if(!parseMethodResult) throw new BadRequestException(); 76 | 77 | this.RequestMethod = parsedMethod; 78 | } 79 | 80 | private void ParseRequestUrl(string[] requestLine) 81 | { 82 | this.Url = requestLine[1]; 83 | } 84 | 85 | private void ParseRequestPath() 86 | { 87 | this.Path = 88 | this.Url.Split(new[] {HttpRequestUrlQuerySeparator, HttpRequestUrlFragmentSeparator}, StringSplitOptions.RemoveEmptyEntries)[0]; 89 | } 90 | 91 | private void ParseHeaders(string[] requestContent) 92 | { 93 | int currentIndex = 0; 94 | 95 | while (!string.IsNullOrEmpty(requestContent[currentIndex])) 96 | { 97 | string[] headerArguments = requestContent[currentIndex++].Split(HttpRequestHeaderNameValueSeparator); 98 | 99 | this.Headers.Add(new HttpHeader(headerArguments[0], headerArguments[1])); 100 | } 101 | 102 | if (!this.Headers.ContainsHeader(GlobalConstants.HostHeaderKey)) 103 | { 104 | throw new BadRequestException(); 105 | } 106 | } 107 | 108 | private void ParseCookies() 109 | { 110 | if (!this.Headers.ContainsHeader(HttpHeader.Cookie)) return; 111 | 112 | string cookiesString = this.Headers.GetHeader(HttpHeader.Cookie).Value; 113 | 114 | if (string.IsNullOrEmpty(cookiesString)) return; 115 | 116 | string[] splitCookies = cookiesString.Split(HttpRequestCookiesSeparator); 117 | 118 | foreach (var splitCookie in splitCookies) 119 | { 120 | string[] cookieParts = splitCookie.Split(HttpRequestCookieNameValueSeparator, 2, StringSplitOptions.RemoveEmptyEntries); 121 | 122 | if (cookieParts.Length != 2) continue; 123 | 124 | string key = cookieParts[0]; 125 | string value = cookieParts[1]; 126 | 127 | this.Cookies.Add(new HttpCookie(key, value, false)); 128 | } 129 | } 130 | 131 | private void ParseQueryParameters() 132 | { 133 | if (!this.Url.Contains('?')) 134 | { 135 | return; 136 | } 137 | 138 | string queryString = this.Url 139 | .Split(new [] {'?', '#'}, StringSplitOptions.None)[1]; 140 | 141 | if (string.IsNullOrWhiteSpace(queryString)) 142 | { 143 | return; 144 | } 145 | 146 | string[] queryParameters = queryString.Split('&'); 147 | 148 | if (!this.IsValidRequestQueryString(queryString, queryParameters)) 149 | { 150 | throw new BadRequestException(); 151 | } 152 | 153 | foreach (var queryParameter in queryParameters) 154 | { 155 | string[] parameterArguments = queryParameter 156 | .Split('=', StringSplitOptions.None); 157 | 158 | this.QueryData.Add(parameterArguments[0], parameterArguments[1]); 159 | } 160 | } 161 | 162 | private void ParseFormDataParameters(string formData) 163 | { 164 | if (string.IsNullOrEmpty(formData)) 165 | { 166 | return; 167 | } 168 | 169 | string[] formDataParams = formData.Split(HttpRequestParameterSeparator); 170 | 171 | foreach (var formDataParameter in formDataParams) 172 | { 173 | string[] parameterArguments = formDataParameter 174 | .Split(HttpRequestParameterNameValueSeparator, StringSplitOptions.None); 175 | 176 | if (this.FormData.ContainsKey(parameterArguments[0])) 177 | { 178 | if (this.FormData[parameterArguments[0]] is string || 179 | !(this.FormData[parameterArguments[0]] is List)) 180 | { 181 | List collection = new List{ this.FormData[parameterArguments[0]].ToString() }; 182 | this.FormData[parameterArguments[0]] = collection; 183 | } 184 | 185 | ((List) this.FormData[parameterArguments[0]]).Add(HttpUtility.UrlDecode(parameterArguments[1])); 186 | } 187 | else 188 | { 189 | this.FormData.Add(parameterArguments[0], HttpUtility.UrlDecode(parameterArguments[1])); 190 | } 191 | } 192 | } 193 | 194 | private void ParseRequestParameters(string formData) 195 | { 196 | this.ParseQueryParameters(); 197 | this.ParseFormDataParameters(formData); 198 | } 199 | 200 | private void ParseRequest(string requestString) 201 | { 202 | string[] splitRequestContent = requestString 203 | .Split(new[] {GlobalConstants.HttpNewLine}, StringSplitOptions.None); 204 | 205 | string[] requestLine = splitRequestContent[0].Trim(). 206 | Split(new [] {' '}, StringSplitOptions.RemoveEmptyEntries); 207 | 208 | if (!this.IsValidRequestLine(requestLine)) 209 | { 210 | throw new BadRequestException(); 211 | } 212 | 213 | this.ParseRequestMethod(requestLine); 214 | this.ParseRequestUrl(requestLine); 215 | this.ParseRequestPath(); 216 | 217 | this.ParseHeaders(splitRequestContent.Skip(1).ToArray()); 218 | this.ParseCookies(); 219 | 220 | this.ParseRequestParameters(splitRequestContent[splitRequestContent.Length - 1]); 221 | } 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /src/SIS.Framework/Routers/ControllerRouter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Reflection; 6 | using SIS.Framework.ActionResults; 7 | using SIS.Framework.Attributes.Action; 8 | using SIS.Framework.Attributes.Method; 9 | using SIS.Framework.Controllers; 10 | using SIS.Framework.Routers.Contracts; 11 | using SIS.Framework.Services; 12 | using SIS.HTTP.Enums; 13 | using SIS.HTTP.Requests; 14 | using SIS.HTTP.Responses; 15 | using SIS.WebServer.Results; 16 | 17 | namespace SIS.Framework.Routers 18 | { 19 | public class ControllerRouter : IMvcRouter 20 | { 21 | private const string DefaultRoute = "/"; 22 | 23 | private const string RequestUrlControllerActionSeparator = "/"; 24 | 25 | private const string DefaultControllerName = "Home"; 26 | 27 | private const string DefaultControllerActionName = "Index"; 28 | 29 | private const string DefaultControllerActionRequestMethod = "GET"; 30 | 31 | private const string UnsupportedActionMessage = "The Action result is not supported."; 32 | 33 | private readonly IDependencyContainer dependencyContainer; 34 | 35 | public ControllerRouter(IDependencyContainer dependencyContainer) 36 | { 37 | this.dependencyContainer = dependencyContainer; 38 | } 39 | 40 | private Controller GetController(string controllerName, IHttpRequest request) 41 | { 42 | if (controllerName != null) 43 | { 44 | string controllerTypeFullName = 45 | $"{MvcContext.Get.AssemblyName}." + 46 | $"{MvcContext.Get.ControllerFolder}." + 47 | $"{controllerName}{MvcContext.Get.ControllersSuffix}, " + 48 | $"{MvcContext.Get.AssemblyName}"; 49 | 50 | Type controllerType = Type.GetType(controllerTypeFullName); 51 | Controller controllerInstance = (Controller)this.dependencyContainer.CreateInstance(controllerType); 52 | 53 | if (controllerInstance != null) 54 | { 55 | controllerInstance.Request = request; 56 | } 57 | 58 | return controllerInstance; 59 | } 60 | 61 | return null; 62 | } 63 | 64 | private string[] ExtractControllerAndActionNames(IHttpRequest request) 65 | { 66 | string[] result = new string[2]; 67 | 68 | if (request.Path == DefaultRoute) 69 | { 70 | result[0] = DefaultControllerName; 71 | result[1] = DefaultControllerActionName; 72 | } 73 | else 74 | { 75 | var requestUrlSplit = request.Path.Split( 76 | RequestUrlControllerActionSeparator 77 | , StringSplitOptions.RemoveEmptyEntries); 78 | 79 | result[0] = requestUrlSplit[0]; 80 | result[1] = requestUrlSplit[1]; 81 | } 82 | 83 | return result; 84 | } 85 | 86 | private IEnumerable GetSuitableMethods(Controller controller, string actionName) 87 | { 88 | if (controller == null) 89 | { 90 | return new MethodInfo[0]; 91 | } 92 | 93 | return controller 94 | .GetType() 95 | .GetMethods() 96 | .Where(m => m.Name.ToLower() == actionName.ToLower()); 97 | } 98 | 99 | private MethodInfo GetMethod(string requestMethod, Controller controller, string actionName) 100 | { 101 | foreach (var controllerAction in this.GetSuitableMethods(controller, actionName)) 102 | { 103 | var controllerActionAttributes = controllerAction 104 | .GetCustomAttributes() 105 | .Where(a => a is HttpMethodAttribute) 106 | .Cast() 107 | .ToList(); 108 | 109 | if (!controllerActionAttributes.Any() 110 | && requestMethod.ToUpper() == DefaultControllerActionRequestMethod) return controllerAction; 111 | 112 | foreach (var attribute in controllerActionAttributes) 113 | { 114 | if (attribute.IsValid(requestMethod)) return controllerAction; 115 | } 116 | } 117 | 118 | return null; 119 | } 120 | 121 | private object GetParameterFromRequestData(IHttpRequest httpRequest, string paramName) 122 | { 123 | if (httpRequest.QueryData.ContainsKey(paramName)) return httpRequest.QueryData[paramName]; 124 | if (httpRequest.FormData.ContainsKey(paramName)) return httpRequest.FormData[paramName]; 125 | return null; 126 | } 127 | 128 | private bool IsValidModel(object bindingModel) 129 | { 130 | foreach (var property in bindingModel.GetType().GetProperties()) 131 | { 132 | IEnumerable validationAttributes 133 | = property.GetCustomAttributes() 134 | .Where(a => a is ValidationAttribute) 135 | .Cast() 136 | .ToList(); 137 | 138 | if (validationAttributes.Any(a => !a.IsValid(property.GetValue(bindingModel)))) 139 | { 140 | return false; 141 | } 142 | } 143 | 144 | return true; 145 | } 146 | 147 | private object ProcessPrimitiveParameter(ParameterInfo param, IHttpRequest httpRequest) 148 | { 149 | object value = this.GetParameterFromRequestData(httpRequest, param.Name); 150 | return Convert.ChangeType(value, param.ParameterType); 151 | } 152 | 153 | private object ProcessBindingModelParameter(ParameterInfo param, IHttpRequest httpRequest) 154 | { 155 | Type bindingModelType = param.ParameterType; 156 | 157 | var bindingModelInstance = Activator.CreateInstance(bindingModelType); 158 | var bindingModelProperties = bindingModelType.GetProperties(); 159 | 160 | foreach (var property in bindingModelProperties) 161 | { 162 | try 163 | { 164 | object value = this.GetParameterFromRequestData(httpRequest, property.Name); 165 | property.SetValue(bindingModelInstance, Convert.ChangeType(value, property.PropertyType)); 166 | } 167 | catch 168 | { 169 | Console.WriteLine($"The {property.Name} field could not be mapped."); 170 | } 171 | } 172 | 173 | return Convert.ChangeType(bindingModelInstance, bindingModelType); 174 | } 175 | 176 | private object[] MapActionParameters(Controller controller, MethodInfo action, IHttpRequest httpRequest) 177 | { 178 | ParameterInfo[] actionParametersInfo = action.GetParameters(); 179 | object[] mappedActionParameters = new object[actionParametersInfo.Length]; 180 | 181 | for (int index = 0; index < actionParametersInfo.Length; index++) 182 | { 183 | ParameterInfo currentParameterInfo = actionParametersInfo[index]; 184 | 185 | if (currentParameterInfo.ParameterType.IsPrimitive 186 | || currentParameterInfo.ParameterType == typeof(string)) 187 | { 188 | mappedActionParameters[index] = ProcessPrimitiveParameter(currentParameterInfo, httpRequest); 189 | } 190 | else 191 | { 192 | object bindingModel = ProcessBindingModelParameter(currentParameterInfo, httpRequest); 193 | controller.ModelState.IsValid = this.IsValidModel(bindingModel); 194 | mappedActionParameters[index] = bindingModel; 195 | } 196 | } 197 | 198 | return mappedActionParameters; 199 | } 200 | 201 | private bool IsAuthorized(Controller controller, MethodInfo action) 202 | => action 203 | .GetCustomAttributes() 204 | .Where(a => a is AuthorizeAttribute) 205 | .Cast() 206 | .All(a => a.IsAuthorized(controller.Identity)); 207 | 208 | private IActionResult InvokeAction(Controller controller, MethodInfo action, object[] actionParameters) 209 | => (IActionResult)action.Invoke(controller, actionParameters); 210 | 211 | private IHttpResponse PrepareResponse(IActionResult actionResult) 212 | { 213 | string invocationResult = actionResult.Invoke(); 214 | 215 | if (actionResult is IViewable) 216 | { 217 | return new HtmlResult(invocationResult, HttpResponseStatusCode.Ok); 218 | } 219 | 220 | if (actionResult is IRedirectable) 221 | { 222 | return new RedirectResult(invocationResult); 223 | } 224 | 225 | throw new InvalidOperationException(UnsupportedActionMessage); 226 | } 227 | 228 | public IHttpResponse Handle(IHttpRequest request) 229 | { 230 | string[] controllerAndActionNames = this.ExtractControllerAndActionNames(request); 231 | 232 | string controllerName = controllerAndActionNames[0]; 233 | string actionName = controllerAndActionNames[1]; 234 | 235 | Controller controller = this.GetController(controllerName, request); 236 | MethodInfo action = this.GetMethod(request.RequestMethod.ToString(), controller, actionName); 237 | 238 | if (controller == null || action == null) 239 | { 240 | return null; 241 | } 242 | 243 | object[] actionParameters = this.MapActionParameters(controller, action, request); 244 | 245 | if (!this.IsAuthorized(controller, action)) 246 | { 247 | return new UnauthorizedResult(); 248 | } 249 | 250 | return this.PrepareResponse(this.InvokeAction(controller, action, actionParameters)); 251 | } 252 | } 253 | } --------------------------------------------------------------------------------