Initial commit
This commit is contained in:
105
SIG.Web/AccessDenied.aspx
Normal file
105
SIG.Web/AccessDenied.aspx
Normal file
@@ -0,0 +1,105 @@
|
||||
<%@ Page Title="Acceso denegado" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" CodeBehind="AccessDenied.aspx.cs" Inherits="SIG.Web.AccessDenied" %>
|
||||
|
||||
<asp:Content ID="ContentHead" ContentPlaceHolderID="H" runat="server">
|
||||
<style type="text/css">
|
||||
.access-denied-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: calc(100vh - 190px);
|
||||
padding: 30px 15px;
|
||||
}
|
||||
|
||||
.access-denied-card {
|
||||
width: 100%;
|
||||
max-width: 680px;
|
||||
padding: 42px 38px;
|
||||
background: #ffffff;
|
||||
border-top: 4px solid #e7505a;
|
||||
box-shadow: 0 4px 18px rgba(0, 0, 0, .10);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.access-denied-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 84px;
|
||||
height: 84px;
|
||||
margin-bottom: 18px;
|
||||
border-radius: 50%;
|
||||
color: #e7505a;
|
||||
background: #fbeaec;
|
||||
font-size: 38px;
|
||||
}
|
||||
|
||||
.access-denied-code {
|
||||
margin: 0 0 4px;
|
||||
color: #e7505a;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.access-denied-title {
|
||||
margin: 0 0 14px;
|
||||
color: #34495e;
|
||||
font-size: 30px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.access-denied-message {
|
||||
max-width: 520px;
|
||||
margin: 0 auto 28px;
|
||||
color: #697882;
|
||||
font-size: 15px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.access-denied-actions .btn {
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.access-denied-card {
|
||||
padding: 32px 20px;
|
||||
}
|
||||
|
||||
.access-denied-title {
|
||||
font-size: 25px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</asp:Content>
|
||||
|
||||
<asp:Content ID="ContentMain" ContentPlaceHolderID="C" runat="server">
|
||||
<div class="access-denied-wrapper">
|
||||
<section class="access-denied-card" aria-labelledby="accessDeniedTitle">
|
||||
<div class="access-denied-icon" aria-hidden="true">
|
||||
<i class="fa fa-lock"></i>
|
||||
</div>
|
||||
|
||||
<p class="access-denied-code">ERROR 403</p>
|
||||
<h1 id="accessDeniedTitle" class="access-denied-title">Acceso denegado</h1>
|
||||
<p class="access-denied-message">
|
||||
Tu sesión está activa, pero tu usuario no cuenta con permiso para consultar esta página.
|
||||
Si necesitas acceso, solicita la autorización correspondiente al administrador del sistema.
|
||||
</p>
|
||||
|
||||
<div class="access-denied-actions">
|
||||
<a class="btn blue" href="<%= ResolveUrl("~/Inicio.aspx") %>">
|
||||
<i class="fa fa-home"></i> Ir al inicio
|
||||
</a>
|
||||
<button type="button" class="btn default" onclick="window.history.back();">
|
||||
<i class="fa fa-arrow-left"></i> Regresar
|
||||
</button>
|
||||
<a class="btn red" href="<%= ResolveUrl("~/Logout.aspx") %>">
|
||||
<i class="fa fa-sign-out"></i> Cerrar sesión
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</asp:Content>
|
||||
|
||||
<asp:Content ID="ContentScripts" ContentPlaceHolderID="J" runat="server">
|
||||
</asp:Content>
|
||||
14
SIG.Web/AccessDenied.aspx.cs
Normal file
14
SIG.Web/AccessDenied.aspx.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Web.UI;
|
||||
|
||||
namespace SIG.Web
|
||||
{
|
||||
public partial class AccessDenied : Page
|
||||
{
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
Response.StatusCode = 403;
|
||||
Response.TrySkipIisCustomErrors = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
6
SIG.Web/AccessDenied.aspx.designer.cs
generated
Normal file
6
SIG.Web/AccessDenied.aspx.designer.cs
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace SIG.Web
|
||||
{
|
||||
public partial class AccessDenied
|
||||
{
|
||||
}
|
||||
}
|
||||
63
SIG.Web/AjaxControlToolkit.config
Normal file
63
SIG.Web/AjaxControlToolkit.config
Normal file
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ajaxControlToolkit>
|
||||
<controlBundles>
|
||||
<controlBundle name="Accordion">
|
||||
<control name="AccordionExtender"></control>
|
||||
<control name="CollapsiblePanelExtender"></control>
|
||||
</controlBundle>
|
||||
<!--<controlBundle name="CustomControls">
|
||||
<control name="AjaxControlToolkitWelcomeLabel.ActWelcomeLabel" assembly="CustomAjaxServerControlSample"></control>
|
||||
<control name="CollapsiblePanelExtender"></control>
|
||||
</controlBundle>-->
|
||||
<controlBundle>
|
||||
<control name="AjaxFileUpload"></control>
|
||||
<control name="AlwaysVisibleControlExtender"></control>
|
||||
<control name="AreaChart"></control>
|
||||
<control name="AsyncFileUpload"></control>
|
||||
<control name="AutoCompleteExtender"></control>
|
||||
<control name="BalloonPopupExtender"></control>
|
||||
<control name="BarChart"></control>
|
||||
<control name="BubbleChart"></control>
|
||||
<control name="CalendarExtender"></control>
|
||||
<control name="CascadingDropDown"></control>
|
||||
<control name="CollapsiblePanelExtender"></control>
|
||||
<control name="ColorPickerExtender"></control>
|
||||
<control name="ComboBox"></control>
|
||||
<control name="ConfirmButtonExtender"></control>
|
||||
<control name="DragPanelExtender"></control>
|
||||
<control name="DropDownExtender"></control>
|
||||
<control name="DropShadowExtender"></control>
|
||||
<control name="DynamicPopulateExtender"></control>
|
||||
<control name="FilteredTextBoxExtender"></control>
|
||||
<control name="Gravatar"></control>
|
||||
<control name="HoverMenuExtender"></control>
|
||||
<control name="HtmlEditor.Editor"></control>
|
||||
<control name="HtmlEditorExtender"></control>
|
||||
<control name="LineChart"></control>
|
||||
<control name="ListSearchExtender"></control>
|
||||
<control name="MaskedEditExtender"></control>
|
||||
<control name="ModalPopupExtender"></control>
|
||||
<control name="MultiHandleSliderExtender"></control>
|
||||
<control name="MutuallyExclusiveCheckBoxExtender"></control>
|
||||
<control name="NoBotExtender"></control>
|
||||
<control name="NumericUpDownExtender"></control>
|
||||
<control name="PagingBulletedListExtender"></control>
|
||||
<control name="PasswordStrength"></control>
|
||||
<control name="PieChart"></control>
|
||||
<control name="PopupControlExtender"></control>
|
||||
<control name="RatingExtender"></control>
|
||||
<control name="ReorderList"></control>
|
||||
<control name="ResizableControlExtender"></control>
|
||||
<control name="RoundedCornersExtender"></control>
|
||||
<control name="Seadragon"></control>
|
||||
<control name="SliderExtender"></control>
|
||||
<control name="SlideShowExtender"></control>
|
||||
<control name="TabContainer"></control>
|
||||
<control name="TextBoxWatermarkExtender"></control>
|
||||
<control name="ToggleButtonExtender"></control>
|
||||
<control name="Twitter"></control>
|
||||
<control name="UpdatePanelAnimationExtender"></control>
|
||||
<control name="ValidatorCalloutExtender"></control>
|
||||
</controlBundle>
|
||||
</controlBundles>
|
||||
</ajaxControlToolkit>
|
||||
59
SIG.Web/AppCode/Configuration/AppServices.cs
Normal file
59
SIG.Web/AppCode/Configuration/AppServices.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using SIG.Web.AppCode.Contracts.Database;
|
||||
using SIG.Web.AppCode.Contracts.Search;
|
||||
using SIG.Web.AppCode.Contracts.Security;
|
||||
using SIG.Web.AppCode.Notifications.Contracts;
|
||||
using SIG.Web.AppCode.Notifications.Services;
|
||||
using SIG.Web.AppCode.Repositories.Security;
|
||||
using SIG.Web.AppCode.Services.Database;
|
||||
using SIG.Web.AppCode.Services.Menu;
|
||||
using SIG.Web.AppCode.Services.Search;
|
||||
using SIG.Web.AppCode.Services.Security;
|
||||
using SIG.Web.AppCode.Session;
|
||||
|
||||
namespace SIG.Web.AppCode.Configuration
|
||||
{
|
||||
public static class AppServices
|
||||
{
|
||||
public static IDbConnectionFactory DbConnectionFactory { get; private set; }
|
||||
public static IDbExecutor DbExecutor { get; private set; }
|
||||
public static IPasswordHasher PasswordHasher { get; private set; }
|
||||
public static IUserRepository UserRepository { get; private set; }
|
||||
public static IUserAdministrationRepository UserAdministrationRepository { get; private set; }
|
||||
public static IPublicRegistrationService PublicRegistrationService { get; private set; }
|
||||
public static ILoginAttemptRepository LoginAttemptRepository { get; private set; } public static IUserSessionRepository UserSessionRepository { get; private set; }
|
||||
public static ILoginService LoginService { get; private set; }
|
||||
public static IUserContextProvider UserContext { get; private set; }
|
||||
public static IPermissionService PermissionService { get; private set; }
|
||||
public static IMenuProvider MenuProvider { get; private set; }
|
||||
public static INotificationCenter NotificationCenter { get; private set; }
|
||||
public static IBuscadorCatalogoDataProvider SearchProvider { get; private set; }
|
||||
|
||||
public static void Configure()
|
||||
{
|
||||
DbConnectionFactory = new DbConnectionFactory();
|
||||
DbExecutor = new DbExecutor(DbConnectionFactory);
|
||||
|
||||
PasswordHasher = new PasswordHasher();
|
||||
|
||||
UserRepository = new UserRepository(DbExecutor);
|
||||
UserAdministrationRepository = new UserAdministrationRepository(DbExecutor);
|
||||
LoginAttemptRepository = new LoginAttemptRepository(DbExecutor); UserSessionRepository = new UserSessionRepository(DbExecutor);
|
||||
|
||||
UserContext = new WebFormsUserContextProvider();
|
||||
PermissionService = new PermissionService(DbExecutor);
|
||||
MenuProvider = new MenuProvider(DbExecutor);
|
||||
NotificationCenter = new DummyNotificationCenter();
|
||||
SearchProvider = new DummyBuscadorCatalogoDataProvider();
|
||||
|
||||
LoginService = new LoginService(
|
||||
UserRepository,
|
||||
PasswordHasher,
|
||||
LoginAttemptRepository,
|
||||
UserSessionRepository);
|
||||
|
||||
PublicRegistrationService = new PublicRegistrationService(
|
||||
UserAdministrationRepository,
|
||||
PasswordHasher,
|
||||
new SmtpEmailSender());
|
||||
} }
|
||||
}
|
||||
127
SIG.Web/AppCode/Configuration/BundleConfig.cs
Normal file
127
SIG.Web/AppCode/Configuration/BundleConfig.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using System.Web.Optimization;
|
||||
using System.Web.UI;
|
||||
|
||||
namespace SIG.Web.AppCode.Configuration
|
||||
{
|
||||
public static class BundleConfig
|
||||
{
|
||||
public static void RegisterBundles(BundleCollection bundles)
|
||||
{
|
||||
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
|
||||
"~/Metronic/assets/global/plugins/jquery.min.js"
|
||||
));
|
||||
|
||||
ScriptManager.ScriptResourceMapping.AddDefinition("jquery", null, new ScriptResourceDefinition
|
||||
{
|
||||
Path = "~/bundles/jquery"
|
||||
});
|
||||
|
||||
bundles.Add(new ScriptBundle("~/bundles/scripts").Include(
|
||||
"~/Metronic/assets/global/plugins/jquery-ui/jquery-ui.min.js",
|
||||
"~/Metronic/assets/global/plugins/jquery-migrate.min.js",
|
||||
"~/Scripts/funciones.js",
|
||||
"~/Scripts/FixFocus.js",
|
||||
"~/Scripts/jquery.number.js",
|
||||
"~/Scripts/jquery.maskMoney.js",
|
||||
"~/Scripts/jquery.thickbox.js",
|
||||
"~/Scripts/jquery.fancybox-1.3.4.js",
|
||||
"~/Scripts/jquery.maskedinput-1.2.2.min.js",
|
||||
"~/Scripts/raphael.2.1.0.min.js",
|
||||
"~/Scripts/justgage.1.0.1.min.js",
|
||||
"~/Scripts/multiselect.js",
|
||||
"~/Scripts/jquery.mask.min.js",
|
||||
"~/Scripts/gridviewScroll.js",
|
||||
"~/Metronic/Carusel/slick.min.js"
|
||||
));
|
||||
|
||||
bundles.Add(new ScriptBundle("~/Metronic/assets/global/scripts/JSV1").Include(
|
||||
"~/Metronic/assets/global/plugins/bootstrap/js/bootstrap.min.js",
|
||||
"~/Metronic/assets/global/plugins/bootstrap-hover-dropdown/bootstrap-hover-dropdown.min.js",
|
||||
"~/Metronic/assets/global/plugins/jquery-slimscroll/jquery.slimscroll.min.js",
|
||||
"~/Metronic/assets/global/plugins/jquery.blockui.min.js",
|
||||
"~/Metronic/assets/global/plugins/jquery.cokie.min.js",
|
||||
"~/Metronic/assets/global/plugins/bootstrap-switch/js/bootstrap-switch.min.js",
|
||||
"~/Metronic/assets/global/plugins/select2/select2.full.min.js",
|
||||
"~/Metronic/assets/global/plugins/select2/i18n/es.js",
|
||||
"~/Metronic/assets/global/plugins/bootstrap-confirmation/bootstrap-confirmation.min.js",
|
||||
"~/Metronic/assets/global/plugins/bootbox/bootbox.min.js",
|
||||
"~/Metronic/assets/global/plugins/bootstrap-toastr/toastr.min.js",
|
||||
"~/Metronic/assets/global/scripts/metronic.js",
|
||||
"~/Metronic/assets/admin/layout/scripts/layout.js",
|
||||
"~/Metronic/assets/admin/layout/scripts/quick-sidebar.js",
|
||||
"~/Metronic/assets/admin/layout/scripts/demo.js",
|
||||
"~/Scripts/porcentaje-box.js",
|
||||
"~/Scripts/DropdownGrid.js",
|
||||
"~/Scripts/UcBuscadorCatalogo.js",
|
||||
"~/Scripts/SigPdfViewer.js",
|
||||
"~/Scripts/SigPdfDrawer.js",
|
||||
"~/Scripts/custom.js"
|
||||
));
|
||||
|
||||
bundles.Add(new ScriptBundle("~/bundles/WebFormsJs").Include(
|
||||
"~/Scripts/WebForms/WebForms.js",
|
||||
"~/Scripts/WebForms/WebUIValidation.js",
|
||||
"~/Scripts/WebForms/MenuStandards.js",
|
||||
"~/Scripts/WebForms/Focus.js",
|
||||
"~/Scripts/WebForms/GridView.js",
|
||||
"~/Scripts/WebForms/DetailsView.js",
|
||||
"~/Scripts/WebForms/TreeView.js",
|
||||
"~/Scripts/WebForms/WebParts.js"
|
||||
));
|
||||
|
||||
ScriptManager.ScriptResourceMapping.AddDefinition("WebFormsBundle", new ScriptResourceDefinition
|
||||
{
|
||||
Path = "~/bundles/WebFormsJs"
|
||||
});
|
||||
|
||||
bundles.Add(new ScriptBundle("~/bundles/MsAjaxJs").Include(
|
||||
"~/Scripts/WebForms/MsAjax/MicrosoftAjax.js",
|
||||
"~/Scripts/WebForms/MsAjax/MicrosoftAjaxApplicationServices.js",
|
||||
"~/Scripts/WebForms/MsAjax/MicrosoftAjaxTimer.js",
|
||||
"~/Scripts/WebForms/MsAjax/MicrosoftAjaxWebForms.js"
|
||||
));
|
||||
|
||||
ScriptManager.ScriptResourceMapping.AddDefinition("MsAjaxBundle", new ScriptResourceDefinition
|
||||
{
|
||||
Path = "~/bundles/MsAjaxJs"
|
||||
});
|
||||
|
||||
bundles.Add(new ScriptBundle("~/bundles/modernizr").Include("~/Scripts/modernizr-*"));
|
||||
|
||||
ScriptManager.ScriptResourceMapping.AddDefinition("respond", new ScriptResourceDefinition
|
||||
{
|
||||
Path = "~/Scripts/respond.min.js",
|
||||
DebugPath = "~/Scripts/respond.js"
|
||||
});
|
||||
|
||||
bundles.Add(new StyleBundle("~/Content/AjaxControlToolkit/Styles/Bundle").Include(
|
||||
"~/Content/AjaxControlToolkit/Styles/AjaxFileUpload.min.css",
|
||||
"~/Content/AjaxControlToolkit/Styles/Calendar.min.css",
|
||||
"~/Content/AjaxControlToolkit/Styles/Tabs.min.css",
|
||||
"~/Content/AjaxControlToolkit/Styles/ValidatorCallout.min.css",
|
||||
"~/Content/AjaxControlToolkit/Styles/HtmlEditorExtender.min.css"
|
||||
));
|
||||
|
||||
bundles.Add(new StyleBundle("~/Metronic/assets/global/css/cssV1")
|
||||
.Include("~/Metronic/assets/global/plugins/font-awesome/css/font-awesome.min.css", new CssRewriteUrlTransform())
|
||||
.Include("~/Metronic/assets/global/plugins/bootstrap/css/bootstrap.min.css", new CssRewriteUrlTransform())
|
||||
.Include("~/Metronic/assets/global/plugins/simple-line-icons/simple-line-icons.min.css", new CssRewriteUrlTransform())
|
||||
.Include(
|
||||
"~/Styles/porcentaje-box.css",
|
||||
"~/Styles/DropdownGrid.css",
|
||||
"~/Styles/UcBuscadorCatalogo.css",
|
||||
"~/Styles/SigPdfViewer.css",
|
||||
"~/Styles/SigPdfDrawer.css",
|
||||
"~/Styles/Estilos2.css",
|
||||
"~/Metronic/assets/global/plugins/bootstrap-switch/css/bootstrap-switch.min.css",
|
||||
"~/Metronic/assets/global/plugins/bootstrap-toastr/toastr.min.css",
|
||||
"~/Metronic/assets/global/plugins/select2/select2.css",
|
||||
"~/Metronic/assets/global/css/components.css",
|
||||
"~/Metronic/assets/global/css/plugins.css",
|
||||
"~/Metronic/assets/admin/layout/css/layout.css",
|
||||
"~/Metronic/assets/admin/layout/css/custom.css")
|
||||
.Include("~/Metronic/Carusel/slick.css", new CssRewriteUrlTransform())
|
||||
.Include("~/Metronic/Carusel/slick-theme.css", new CssRewriteUrlTransform()));
|
||||
}
|
||||
}
|
||||
}
|
||||
294
SIG.Web/AppCode/Configuration/ErrorHandlerConfig.cs
Normal file
294
SIG.Web/AppCode/Configuration/ErrorHandlerConfig.cs
Normal file
@@ -0,0 +1,294 @@
|
||||
using System;
|
||||
using System.Data.SqlClient;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using SIG.Web.AppCode.Services.Errors;
|
||||
using SIG.Web.AppCode.Session;
|
||||
|
||||
namespace SIG.Web.AppCode.Configuration
|
||||
{
|
||||
public static class ErrorHandlerConfig
|
||||
{
|
||||
public static void Register()
|
||||
{
|
||||
}
|
||||
|
||||
public static void HandleServerError(
|
||||
Exception exception,
|
||||
HttpContext context)
|
||||
{
|
||||
if (exception == null || context == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
UserSessionModel usuario = ObtenerUsuarioActual();
|
||||
|
||||
string cveUsuario = usuario != null
|
||||
? usuario.CveUsuario.ToString()
|
||||
: string.Empty;
|
||||
|
||||
string nombreUsuario = usuario != null
|
||||
? usuario.Nombre
|
||||
: string.Empty;
|
||||
|
||||
int cveRole = usuario != null
|
||||
? usuario.CveRol
|
||||
: 0;
|
||||
|
||||
string codigo = SIGErrorManager.Registrar(
|
||||
exception,
|
||||
context,
|
||||
"Application_Error",
|
||||
cveUsuario,
|
||||
nombreUsuario
|
||||
);
|
||||
|
||||
bool esPeticionAsincrona =
|
||||
SIGErrorManager.EsPeticionAsincrona(context);
|
||||
|
||||
/*
|
||||
* El detalle t<>cnico completo siempre se guarda en log.
|
||||
* Solo el administrador puede verlo en la respuesta.
|
||||
*/
|
||||
bool mostrarDetalle = cveRole == 1;
|
||||
|
||||
context.Server.ClearError();
|
||||
|
||||
if (esPeticionAsincrona)
|
||||
{
|
||||
EscribirErrorAsincrono(
|
||||
context,
|
||||
codigo,
|
||||
exception,
|
||||
mostrarDetalle
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
EscribirPaginaError(
|
||||
context,
|
||||
codigo,
|
||||
exception,
|
||||
mostrarDetalle,
|
||||
cveUsuario,
|
||||
nombreUsuario,
|
||||
cveRole
|
||||
);
|
||||
}
|
||||
catch (Exception handlerException)
|
||||
{
|
||||
try
|
||||
{
|
||||
SIGErrorManager.Registrar(
|
||||
handlerException,
|
||||
context,
|
||||
"ErrorHandlerConfig.HandleServerError",
|
||||
string.Empty,
|
||||
string.Empty
|
||||
);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// El manejador global nunca debe provocar otro error.
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
context.Server.ClearError();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetFriendlyMessage(
|
||||
Exception exception)
|
||||
{
|
||||
if (exception == null)
|
||||
{
|
||||
return "Ocurri<72> un error inesperado.";
|
||||
}
|
||||
|
||||
Exception baseException =
|
||||
exception.GetBaseException();
|
||||
|
||||
SqlException sqlException =
|
||||
baseException as SqlException;
|
||||
|
||||
if (sqlException != null)
|
||||
{
|
||||
switch (sqlException.Number)
|
||||
{
|
||||
case 207:
|
||||
return "No fue posible completar la operaci<63>n porque falta una columna requerida en la base de datos.";
|
||||
|
||||
case 208:
|
||||
return "No fue posible completar la operaci<63>n porque falta una tabla o vista requerida en la base de datos.";
|
||||
|
||||
case 4060:
|
||||
return "No fue posible abrir la base de datos configurada.";
|
||||
|
||||
case 18456:
|
||||
return "No fue posible iniciar sesi<73>n en la base de datos.";
|
||||
|
||||
case -2:
|
||||
return "La base de datos tard<72> demasiado en responder.";
|
||||
|
||||
case 1205:
|
||||
return "La operaci<63>n no pudo completarse porque ocurri<72> un bloqueo en la base de datos. Intente nuevamente.";
|
||||
|
||||
case 547:
|
||||
return "No fue posible completar la operaci<63>n porque existen registros relacionados.";
|
||||
|
||||
case 2627:
|
||||
case 2601:
|
||||
return "Ya existe un registro con la misma informaci<63>n.";
|
||||
|
||||
default:
|
||||
return "Ocurri<72> un error al consultar la base de datos.";
|
||||
}
|
||||
}
|
||||
|
||||
if (baseException is TimeoutException)
|
||||
{
|
||||
return "La solicitud tard<72> demasiado en responder. Intente nuevamente.";
|
||||
}
|
||||
|
||||
if (baseException is OperationCanceledException)
|
||||
{
|
||||
return "La operaci<63>n fue cancelada antes de completarse.";
|
||||
}
|
||||
|
||||
HttpException httpException =
|
||||
baseException as HttpException;
|
||||
|
||||
if (httpException != null)
|
||||
{
|
||||
int statusCode =
|
||||
httpException.GetHttpCode();
|
||||
|
||||
switch (statusCode)
|
||||
{
|
||||
case 400:
|
||||
return "La solicitud enviada no es v<>lida.";
|
||||
|
||||
case 401:
|
||||
return "Su sesi<73>n no tiene autorizaci<63>n para realizar esta operaci<63>n.";
|
||||
|
||||
case 403:
|
||||
return "No tiene permisos para realizar esta operaci<63>n.";
|
||||
|
||||
case 404:
|
||||
return "No se encontr<74> el recurso solicitado.";
|
||||
|
||||
case 408:
|
||||
return "La solicitud tard<72> demasiado en responder.";
|
||||
|
||||
case 413:
|
||||
return "El archivo o contenido enviado supera el tama<6D>o permitido.";
|
||||
|
||||
default:
|
||||
return "Ocurri<72> un error al procesar la solicitud.";
|
||||
}
|
||||
}
|
||||
|
||||
return "Ocurri<72> un error inesperado. Intente nuevamente.";
|
||||
}
|
||||
|
||||
private static void EscribirErrorAsincrono(
|
||||
HttpContext context,
|
||||
string codigo,
|
||||
Exception exception,
|
||||
bool mostrarDetalle)
|
||||
{
|
||||
string respuesta =
|
||||
"SIG_ERROR|" + codigo;
|
||||
|
||||
if (mostrarDetalle)
|
||||
{
|
||||
string detalle =
|
||||
SIGErrorManager.ObtenerDetalleTecnico(
|
||||
exception
|
||||
);
|
||||
|
||||
string detalleBase64 =
|
||||
Convert.ToBase64String(
|
||||
Encoding.UTF8.GetBytes(detalle)
|
||||
);
|
||||
|
||||
respuesta += "|" + detalleBase64;
|
||||
}
|
||||
|
||||
context.Response.Clear();
|
||||
context.Response.ClearHeaders();
|
||||
|
||||
context.Response.StatusCode = 500;
|
||||
context.Response.TrySkipIisCustomErrors = true;
|
||||
context.Response.ContentType =
|
||||
"text/plain; charset=utf-8";
|
||||
|
||||
context.Response.AddHeader(
|
||||
"X-SIG-Error-Code",
|
||||
codigo
|
||||
);
|
||||
|
||||
context.Response.Write(respuesta);
|
||||
|
||||
context.ApplicationInstance.CompleteRequest();
|
||||
}
|
||||
|
||||
private static void EscribirPaginaError(
|
||||
HttpContext context,
|
||||
string codigo,
|
||||
Exception exception,
|
||||
bool mostrarDetalle,
|
||||
string cveUsuario,
|
||||
string nombreUsuario,
|
||||
int cveRole)
|
||||
{
|
||||
string html =
|
||||
SIGErrorManager.CrearHtmlError(
|
||||
codigo,
|
||||
exception,
|
||||
mostrarDetalle,
|
||||
cveUsuario,
|
||||
nombreUsuario,
|
||||
cveRole
|
||||
);
|
||||
|
||||
context.Response.Clear();
|
||||
context.Response.ClearHeaders();
|
||||
|
||||
context.Response.StatusCode = 500;
|
||||
context.Response.TrySkipIisCustomErrors = true;
|
||||
context.Response.ContentType =
|
||||
"text/html; charset=utf-8";
|
||||
|
||||
context.Response.Write(html);
|
||||
|
||||
context.ApplicationInstance.CompleteRequest();
|
||||
}
|
||||
|
||||
private static UserSessionModel ObtenerUsuarioActual()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (AppServices.UserContext == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return AppServices.UserContext.GetCurrentUser();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
8
SIG.Web/AppCode/Contracts/Data/DbProviderKind.cs
Normal file
8
SIG.Web/AppCode/Contracts/Data/DbProviderKind.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace SIG.Web.AppCode.Contracts.Data
|
||||
{
|
||||
public enum DbProviderKind
|
||||
{
|
||||
SqlServer,
|
||||
PostgreSql
|
||||
}
|
||||
}
|
||||
10
SIG.Web/AppCode/Contracts/Data/DomainConnectionSettings.cs
Normal file
10
SIG.Web/AppCode/Contracts/Data/DomainConnectionSettings.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace SIG.Web.AppCode.Contracts.Data
|
||||
{
|
||||
public sealed class DomainConnectionSettings
|
||||
{
|
||||
public string Domain { get; set; }
|
||||
public DbProviderKind Provider { get; set; }
|
||||
public string ConnectionStringName { get; set; }
|
||||
public bool IsDefault { get; set; }
|
||||
}
|
||||
}
|
||||
9
SIG.Web/AppCode/Contracts/Data/IDbConnectionFactory.cs
Normal file
9
SIG.Web/AppCode/Contracts/Data/IDbConnectionFactory.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System.Data;
|
||||
|
||||
namespace SIG.Web.AppCode.Contracts.Data
|
||||
{
|
||||
public interface IDbConnectionFactory
|
||||
{
|
||||
IDbConnection Create(string domain);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace SIG.Web.AppCode.Contracts.Data
|
||||
{
|
||||
public interface IDomainConnectionResolver
|
||||
{
|
||||
DomainConnectionSettings Resolve(string domain);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
using System.Data;
|
||||
namespace SIG.Web.AppCode.Contracts.Database
|
||||
{
|
||||
public interface IDbConnectionFactory { IDbConnection CreateConnection(string domainName); }
|
||||
}
|
||||
11
SIG.Web/AppCode/Contracts/Database/IDbExecutor.cs
Normal file
11
SIG.Web/AppCode/Contracts/Database/IDbExecutor.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using SIG.Web.AppCode.Models.Common;
|
||||
|
||||
namespace SIG.Web.AppCode.Contracts.Database
|
||||
{
|
||||
public interface IDbExecutor
|
||||
{
|
||||
DbResult<T> Execute<T>(string domainName, Func<IDbConnection, T> action);
|
||||
}
|
||||
}
|
||||
7
SIG.Web/AppCode/Contracts/Login/ILoginService.cs
Normal file
7
SIG.Web/AppCode/Contracts/Login/ILoginService.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace SIG.Web.AppCode.Contracts.Login
|
||||
{
|
||||
public interface ILoginService
|
||||
{
|
||||
LoginResult Login(string userName, string password, string domain);
|
||||
}
|
||||
}
|
||||
21
SIG.Web/AppCode/Contracts/Login/LoginResult.cs
Normal file
21
SIG.Web/AppCode/Contracts/Login/LoginResult.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using SIG.Web.AppCode.Session;
|
||||
|
||||
namespace SIG.Web.AppCode.Contracts.Login
|
||||
{
|
||||
public sealed class LoginResult
|
||||
{
|
||||
public bool Exitoso { get; private set; }
|
||||
public string Mensaje { get; private set; }
|
||||
public UserSessionModel Usuario { get; private set; }
|
||||
|
||||
public static LoginResult Ok(UserSessionModel usuario)
|
||||
{
|
||||
return new LoginResult { Exitoso = true, Usuario = usuario, Mensaje = "OK" };
|
||||
}
|
||||
|
||||
public static LoginResult Fail(string mensaje)
|
||||
{
|
||||
return new LoginResult { Exitoso = false, Mensaje = mensaje };
|
||||
}
|
||||
}
|
||||
}
|
||||
45
SIG.Web/AppCode/Contracts/Search/BuscadorCatalogoModels.cs
Normal file
45
SIG.Web/AppCode/Contracts/Search/BuscadorCatalogoModels.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SIG.Web.AppCode.Contracts.Search
|
||||
{
|
||||
public sealed class BuscadorCatalogoFiltro
|
||||
{
|
||||
public string Campo { get; set; }
|
||||
public string Operador { get; set; }
|
||||
public string Valor { get; set; }
|
||||
}
|
||||
|
||||
public sealed class BuscadorCatalogoOrden
|
||||
{
|
||||
public string Campo { get; set; }
|
||||
public string Direccion { get; set; } = "ASC";
|
||||
}
|
||||
|
||||
public sealed class BuscadorCatalogoColumna
|
||||
{
|
||||
public string Campo { get; set; }
|
||||
public string Titulo { get; set; }
|
||||
public bool Visible { get; set; } = true;
|
||||
public bool Busqueda { get; set; } = true;
|
||||
public string CssClass { get; set; }
|
||||
}
|
||||
|
||||
public sealed class BuscadorCatalogoCallbackSolicitud
|
||||
{
|
||||
public string Catalogo { get; set; }
|
||||
public string Termino { get; set; }
|
||||
public int Pagina { get; set; } = 1;
|
||||
public int TamanoPagina { get; set; } = 20;
|
||||
public List<BuscadorCatalogoFiltro> Filtros { get; set; } = new List<BuscadorCatalogoFiltro>();
|
||||
public List<BuscadorCatalogoOrden> Orden { get; set; } = new List<BuscadorCatalogoOrden>();
|
||||
}
|
||||
|
||||
public sealed class BuscadorCatalogoCallbackRespuesta
|
||||
{
|
||||
public bool Success { get; set; } = true;
|
||||
public string Message { get; set; }
|
||||
public int Total { get; set; }
|
||||
public List<Dictionary<string, object>> Rows { get; set; } = new List<Dictionary<string, object>>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using SIG.Web.AppCode.Session;
|
||||
|
||||
namespace SIG.Web.AppCode.Contracts.Search
|
||||
{
|
||||
public interface IBuscadorCatalogoDataProvider
|
||||
{
|
||||
BuscadorCatalogoCallbackRespuesta Search(BuscadorCatalogoCallbackSolicitud request, UserSessionModel user);
|
||||
}
|
||||
}
|
||||
8
SIG.Web/AppCode/Contracts/Security/IEmailSender.cs
Normal file
8
SIG.Web/AppCode/Contracts/Security/IEmailSender.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace SIG.Web.AppCode.Contracts.Security
|
||||
{
|
||||
public interface IEmailSender
|
||||
{
|
||||
bool IsConfigured { get; }
|
||||
void SendVerification(string recipient, string verificationUrl);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using SIG.Web.AppCode.Models.Common;
|
||||
|
||||
namespace SIG.Web.AppCode.Contracts.Security
|
||||
{
|
||||
public interface ILoginAttemptRepository
|
||||
{
|
||||
DbResult<bool> Save(int? cveUser, string username, bool wasSuccessful, string failureReason, string ipAddress, string userAgent, string domainName);
|
||||
}
|
||||
}
|
||||
9
SIG.Web/AppCode/Contracts/Security/ILoginService.cs
Normal file
9
SIG.Web/AppCode/Contracts/Security/ILoginService.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using SIG.Web.AppCode.Models.Security;
|
||||
namespace SIG.Web.AppCode.Contracts.Security
|
||||
{
|
||||
public interface ILoginService
|
||||
{
|
||||
LoginResult Login(LoginRequest request);
|
||||
void Logout(string sessionToken, string domainName);
|
||||
}
|
||||
}
|
||||
8
SIG.Web/AppCode/Contracts/Security/IPasswordHasher.cs
Normal file
8
SIG.Web/AppCode/Contracts/Security/IPasswordHasher.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace SIG.Web.AppCode.Contracts.Security
|
||||
{
|
||||
public interface IPasswordHasher
|
||||
{
|
||||
string HashPassword(string password);
|
||||
bool VerifyPassword(string password, string storedHash);
|
||||
}
|
||||
}
|
||||
8
SIG.Web/AppCode/Contracts/Security/IPermissionService.cs
Normal file
8
SIG.Web/AppCode/Contracts/Security/IPermissionService.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace SIG.Web.AppCode.Contracts.Security
|
||||
{
|
||||
public interface IPermissionService
|
||||
{
|
||||
bool HasPermission(int cveUser, string permissionCode);
|
||||
bool CanAccessPage(int cveUser, string pageUrl);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Collections.Generic;
|
||||
using SIG.Web.AppCode.Models.Common;
|
||||
using SIG.Web.AppCode.Models.Security;
|
||||
|
||||
namespace SIG.Web.AppCode.Contracts.Security
|
||||
{
|
||||
public interface IPublicRegistrationService
|
||||
{
|
||||
DbResult<bool> Validate(PublicRegistrationDraft draft, string domainName);
|
||||
DbResult<List<StateOptionModel>> ListStates(string domainName);
|
||||
DbResult<bool> Register(PublicRegistrationDraft draft, string verificationBaseUrl, string domainName);
|
||||
DbResult<bool> Verify(string token, string domainName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System.Collections.Generic;
|
||||
using SIG.Web.AppCode.Models.Common;
|
||||
using SIG.Web.AppCode.Models.Security;
|
||||
|
||||
namespace SIG.Web.AppCode.Contracts.Security
|
||||
{
|
||||
public interface IUserAdministrationRepository
|
||||
{
|
||||
DbResult<List<UserAdministrationModel>> ListUsers(string text, int executingUser, string domainName);
|
||||
DbResult<UserAdministrationModel> GetUser(int userId, int executingUser, string domainName);
|
||||
DbResult<List<RoleOptionModel>> ListRoles(int executingUser, string domainName);
|
||||
DbResult<List<StateOptionModel>> ListStates(string domainName);
|
||||
DbResult<int> SaveUser(UserAdministrationModel user, string passwordHash, int executingUser, string domainName);
|
||||
DbResult<bool> DeleteUser(int userId, int executingUser, string domainName);
|
||||
DbResult<List<UserClientModel>> ListUserClients(int userId, int executingUser, string domainName);
|
||||
DbResult<bool> SaveUserClient(int userId, int clientId, int order, bool primary, int executingUser, string domainName);
|
||||
DbResult<bool> ValidateRegistration(string username, string email, string rfc, string domainName);
|
||||
DbResult<RegistrationCreatedModel> CreateRegistration(PublicRegistrationDraft draft, string passwordHash, string tokenHash, string domainName);
|
||||
DbResult<bool> VerifyEmail(string tokenHash, string domainName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
using SIG.Web.AppCode.Session;
|
||||
namespace SIG.Web.AppCode.Contracts.Security
|
||||
{
|
||||
public interface IUserContextProvider
|
||||
{
|
||||
bool IsAuthenticated(); UserSessionModel GetCurrentUser(); void SetCurrentUser(UserSessionModel user); void Clear();
|
||||
}
|
||||
}
|
||||
12
SIG.Web/AppCode/Contracts/Security/IUserRepository.cs
Normal file
12
SIG.Web/AppCode/Contracts/Security/IUserRepository.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using SIG.Web.AppCode.Models.Common;
|
||||
using SIG.Web.AppCode.Models.Security;
|
||||
|
||||
namespace SIG.Web.AppCode.Contracts.Security
|
||||
{
|
||||
public interface IUserRepository
|
||||
{
|
||||
DbResult<UserModel> ObtenerPorNombreUsuario(string username, string domainName);
|
||||
DbResult<bool> RegisterSuccessfulLogin(int cveUser, string domainName);
|
||||
DbResult<bool> RegisterFailedLogin(int cveUser, string domainName);
|
||||
}
|
||||
}
|
||||
10
SIG.Web/AppCode/Contracts/Security/IUserSessionRepository.cs
Normal file
10
SIG.Web/AppCode/Contracts/Security/IUserSessionRepository.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using SIG.Web.AppCode.Models.Common;
|
||||
|
||||
namespace SIG.Web.AppCode.Contracts.Security
|
||||
{
|
||||
public interface IUserSessionRepository
|
||||
{
|
||||
DbResult<bool> StartSession(int cveUser, string sessionToken, string ipAddress, string userAgent, string domainName);
|
||||
DbResult<bool> EndSession(string sessionToken, string domainName);
|
||||
}
|
||||
}
|
||||
37
SIG.Web/AppCode/Models/Common/DbResult.cs
Normal file
37
SIG.Web/AppCode/Models/Common/DbResult.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
|
||||
namespace SIG.Web.AppCode.Models.Common
|
||||
{
|
||||
public sealed class DbResult<T>
|
||||
{
|
||||
public bool Success { get; private set; }
|
||||
public T Data { get; private set; }
|
||||
public string UserMessage { get; private set; }
|
||||
public string TechnicalMessage { get; private set; }
|
||||
public Exception Exception { get; private set; }
|
||||
|
||||
public static DbResult<T> Ok(T data)
|
||||
{
|
||||
return new DbResult<T>
|
||||
{
|
||||
Success = true,
|
||||
Data = data,
|
||||
UserMessage = string.Empty,
|
||||
TechnicalMessage = string.Empty,
|
||||
Exception = null
|
||||
};
|
||||
}
|
||||
|
||||
public static DbResult<T> Fail(string userMessage, string technicalMessage, Exception exception)
|
||||
{
|
||||
return new DbResult<T>
|
||||
{
|
||||
Success = false,
|
||||
Data = default(T),
|
||||
UserMessage = userMessage,
|
||||
TechnicalMessage = technicalMessage,
|
||||
Exception = exception
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
9
SIG.Web/AppCode/Models/Common/StoredProcedureResult.cs
Normal file
9
SIG.Web/AppCode/Models/Common/StoredProcedureResult.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace SIG.Web.AppCode.Models.Common
|
||||
{
|
||||
public sealed class StoredProcedureResult
|
||||
{
|
||||
public bool Return { get; set; }
|
||||
public int ErrorTipo { get; set; }
|
||||
public string ErrorMostrar { get; set; }
|
||||
}
|
||||
}
|
||||
11
SIG.Web/AppCode/Models/Security/LoginRequest.cs
Normal file
11
SIG.Web/AppCode/Models/Security/LoginRequest.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace SIG.Web.AppCode.Models.Security
|
||||
{
|
||||
public sealed class LoginRequest
|
||||
{
|
||||
public string NombreUsuario { get; set; }
|
||||
public string Contrasena { get; set; }
|
||||
public string NombreDominio { get; set; }
|
||||
public string DireccionIP { get; set; }
|
||||
public string AgenteUsuario { get; set; }
|
||||
}
|
||||
}
|
||||
12
SIG.Web/AppCode/Models/Security/LoginResult.cs
Normal file
12
SIG.Web/AppCode/Models/Security/LoginResult.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using SIG.Web.AppCode.Session;
|
||||
namespace SIG.Web.AppCode.Models.Security
|
||||
{
|
||||
public sealed class LoginResult
|
||||
{
|
||||
public bool Exitoso { get; private set; }
|
||||
public string Mensaje { get; private set; }
|
||||
public UserSessionModel Usuario { get; private set; }
|
||||
public static LoginResult Ok(UserSessionModel usuario) { return new LoginResult { Exitoso = true, Usuario = usuario, Mensaje = string.Empty }; }
|
||||
public static LoginResult Fail(string mensaje) { return new LoginResult { Exitoso = false, Mensaje = mensaje }; }
|
||||
}
|
||||
}
|
||||
70
SIG.Web/AppCode/Models/Security/UserAdministrationModels.cs
Normal file
70
SIG.Web/AppCode/Models/Security/UserAdministrationModels.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
|
||||
namespace SIG.Web.AppCode.Models.Security
|
||||
{
|
||||
[Serializable]
|
||||
public sealed class PublicRegistrationDraft
|
||||
{
|
||||
public string NombreUsuario { get; set; }
|
||||
public string CorreoElectronico { get; set; }
|
||||
public string Contrasena { get; set; }
|
||||
public string RFC { get; set; }
|
||||
public string RazonSocial { get; set; }
|
||||
public string NombreComercial { get; set; }
|
||||
public string Direccion { get; set; }
|
||||
public string NoExt { get; set; }
|
||||
public string NoInt { get; set; }
|
||||
public string Colonia { get; set; }
|
||||
public string Ciudad { get; set; }
|
||||
public int CveSATEstado { get; set; }
|
||||
public string Pais { get; set; }
|
||||
public string CP { get; set; }
|
||||
public string Telefono { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UserAdministrationModel
|
||||
{
|
||||
public int CveUsuario { get; set; }
|
||||
public string NombreUsuario { get; set; }
|
||||
public string CorreoElectronico { get; set; }
|
||||
public int CveRol { get; set; }
|
||||
public string NombreRol { get; set; }
|
||||
public bool Activo { get; set; }
|
||||
public bool DebeCambiarContrasena { get; set; }
|
||||
public bool CorreoVerificado { get; set; }
|
||||
public DateTime FechaCreacion { get; set; }
|
||||
}
|
||||
|
||||
public sealed class RoleOptionModel
|
||||
{
|
||||
public int CveRol { get; set; }
|
||||
public string NombreRol { get; set; }
|
||||
public string Descripcion { get; set; }
|
||||
}
|
||||
|
||||
public sealed class StateOptionModel
|
||||
{
|
||||
public int CveEstado { get; set; }
|
||||
public string ClaveEstado { get; set; }
|
||||
public string NombreEstado { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UserClientModel
|
||||
{
|
||||
public int CveUsuarioCliente { get; set; }
|
||||
public int CveUsuario { get; set; }
|
||||
public int CveCliente { get; set; }
|
||||
public string Nombre { get; set; }
|
||||
public string RFC { get; set; }
|
||||
public int Orden { get; set; }
|
||||
public bool EsPrincipal { get; set; }
|
||||
public bool Activo { get; set; }
|
||||
public bool ClienteActivo { get; set; }
|
||||
}
|
||||
|
||||
public sealed class RegistrationCreatedModel
|
||||
{
|
||||
public int CveUsuario { get; set; }
|
||||
public int CveCliente { get; set; }
|
||||
}
|
||||
}
|
||||
19
SIG.Web/AppCode/Models/Security/UserModel.cs
Normal file
19
SIG.Web/AppCode/Models/Security/UserModel.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
namespace SIG.Web.AppCode.Models.Security
|
||||
{
|
||||
public sealed class UserModel
|
||||
{
|
||||
public int CveUsuario { get; set; }
|
||||
public string NombreUsuario { get; set; }
|
||||
public string HashContrasena { get; set; }
|
||||
public string CorreoElectronico { get; set; }
|
||||
public int CveRol { get; set; }
|
||||
public string NombreRol { get; set; }
|
||||
public bool Activo { get; set; }
|
||||
public bool CorreoVerificado { get; set; }
|
||||
public bool DebeCambiarContrasena { get; set; } public int IntentosFallidosInicioSesion { get; set; }
|
||||
public DateTime? BloqueadoHasta { get; set; }
|
||||
public DateTime? FechaUltimoInicioSesion { get; set; }
|
||||
public DateTime? FechaUltimaConexion { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using SIG.Web.AppCode.Notifications.Models;
|
||||
|
||||
namespace SIG.Web.AppCode.Notifications.Contracts
|
||||
{
|
||||
public interface INotificationCenter
|
||||
{
|
||||
void Queue(NotificationMessage message);
|
||||
void MarkAsShown(int notificationId, int userId);
|
||||
IReadOnlyList<NotificationMessage> GetBellMessages(int userId);
|
||||
int CountPending(int userId);
|
||||
}
|
||||
}
|
||||
12
SIG.Web/AppCode/Notifications/Hubs/NotificationHub.cs
Normal file
12
SIG.Web/AppCode/Notifications/Hubs/NotificationHub.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Microsoft.AspNet.SignalR;
|
||||
|
||||
namespace SIG.Web.AppCode.Notifications.Hubs
|
||||
{
|
||||
public class NotificationHub : Hub
|
||||
{
|
||||
public void Ping()
|
||||
{
|
||||
Clients.Caller.pong("SignalR is working.");
|
||||
}
|
||||
}
|
||||
}
|
||||
22
SIG.Web/AppCode/Notifications/Models/NotificationMessage.cs
Normal file
22
SIG.Web/AppCode/Notifications/Models/NotificationMessage.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
namespace SIG.Web.AppCode.Notifications.Models
|
||||
{
|
||||
[Serializable]
|
||||
public sealed class NotificationMessage
|
||||
{
|
||||
public int CveNotificacion { get; set; }
|
||||
public int CveUsuario { get; set; }
|
||||
public string TipoNotificacion { get; set; }
|
||||
public string EstadoNotificacion { get; set; }
|
||||
public string Titulo { get; set; }
|
||||
public string Mensaje { get; set; }
|
||||
public string Url { get; set; }
|
||||
public bool Leida { get; set; }
|
||||
public bool MostradaEnCampana { get; set; }
|
||||
public DateTime? FechaEnvio { get; set; }
|
||||
public DateTime? FechaLectura { get; set; }
|
||||
public DateTime FechaCreacion { get; set; }
|
||||
public DateTime? FechaActualizacion { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using SIG.Web.AppCode.Notifications.Contracts;
|
||||
using SIG.Web.AppCode.Notifications.Models;
|
||||
|
||||
namespace SIG.Web.AppCode.Notifications.Services
|
||||
{
|
||||
public sealed class DummyNotificationCenter : INotificationCenter
|
||||
{
|
||||
private static readonly List<NotificationMessage> Items = new List<NotificationMessage>();
|
||||
|
||||
public void Queue(NotificationMessage message)
|
||||
{
|
||||
if (message == null) return;
|
||||
message.CveNotificacion = Items.Count + 1;
|
||||
message.FechaCreacion = System.DateTime.Now;
|
||||
message.EstadoNotificacion = "Pendiente";
|
||||
Items.Add(message);
|
||||
}
|
||||
|
||||
public void MarkAsShown(int notificationId, int userId)
|
||||
{
|
||||
var item = Items.FirstOrDefault(x => x.CveNotificacion == notificationId && x.CveUsuario == userId);
|
||||
if (item == null) return;
|
||||
item.MostradaEnCampana = true;
|
||||
}
|
||||
|
||||
public IReadOnlyList<NotificationMessage> GetBellMessages(int userId)
|
||||
{
|
||||
return Items.Where(x => x.CveUsuario == userId && !x.Leida).ToList();
|
||||
}
|
||||
|
||||
public int CountPending(int userId)
|
||||
{
|
||||
return GetBellMessages(userId).Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Data;
|
||||
using SIG.Web.AppCode.Contracts.Database;
|
||||
using SIG.Web.AppCode.Contracts.Security;
|
||||
using SIG.Web.AppCode.Models.Common;
|
||||
using SIG.Web.AppCode.Services.Database;
|
||||
|
||||
namespace SIG.Web.AppCode.Repositories.Security
|
||||
{
|
||||
public sealed class LoginAttemptRepository : ILoginAttemptRepository
|
||||
{
|
||||
private readonly IDbExecutor _dbExecutor;
|
||||
|
||||
public LoginAttemptRepository(IDbExecutor dbExecutor)
|
||||
{
|
||||
_dbExecutor = dbExecutor;
|
||||
}
|
||||
|
||||
public DbResult<bool> Save(int? cveUser, string username, bool wasSuccessful, string failureReason, string ipAddress, string userAgent, string domainName)
|
||||
{
|
||||
return _dbExecutor.Execute(domainName, connection =>
|
||||
{
|
||||
using (IDbCommand command = connection.CreateCommand())
|
||||
{
|
||||
StoredProcedureCommand.Configure(command, "dbo.PA_Sis_IntentoInicioSesionAdd", cveUser ?? 0);
|
||||
StoredProcedureCommand.AddInput(command, "@CveUsuario", cveUser.HasValue ? (object)cveUser.Value : null, DbType.Int32);
|
||||
StoredProcedureCommand.AddInput(command, "@NombreUsuario", username, DbType.String);
|
||||
StoredProcedureCommand.AddInput(command, "@Exitoso", wasSuccessful, DbType.Boolean);
|
||||
StoredProcedureCommand.AddInput(command, "@MotivoFallo", failureReason, DbType.String);
|
||||
StoredProcedureCommand.AddInput(command, "@DireccionIP", ipAddress, DbType.String);
|
||||
StoredProcedureCommand.AddInput(command, "@AgenteUsuario", userAgent, DbType.String);
|
||||
StoredProcedureCommand.AddInput(command, "@NombreDominio", domainName, DbType.String);
|
||||
|
||||
command.ExecuteNonQuery();
|
||||
StoredProcedureCommand.EnsureSuccess(command);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using SIG.Web.AppCode.Contracts.Database;
|
||||
using SIG.Web.AppCode.Contracts.Security;
|
||||
using SIG.Web.AppCode.Models.Common;
|
||||
using SIG.Web.AppCode.Models.Security;
|
||||
using SIG.Web.AppCode.Services.Database;
|
||||
using SIG.Web.AppCode.Utilities;
|
||||
|
||||
namespace SIG.Web.AppCode.Repositories.Security
|
||||
{
|
||||
public sealed class UserAdministrationRepository : IUserAdministrationRepository
|
||||
{
|
||||
private readonly IDbExecutor _db;
|
||||
public UserAdministrationRepository(IDbExecutor db) { _db = db; }
|
||||
|
||||
public DbResult<List<UserAdministrationModel>> ListUsers(string text, int executingUser, string domainName)
|
||||
{ return QueryList<UserAdministrationModel>("dbo.PA_Sis_UsuarioList", executingUser, domainName, c => StoredProcedureCommand.AddInput(c, "@Texto", text, DbType.String)); }
|
||||
|
||||
public DbResult<UserAdministrationModel> GetUser(int userId, int executingUser, string domainName)
|
||||
{ return QueryOne<UserAdministrationModel>("dbo.PA_Sis_UsuarioGet", executingUser, domainName, c => StoredProcedureCommand.AddInput(c, "@CveUsuario", userId, DbType.Int32)); }
|
||||
|
||||
public DbResult<List<RoleOptionModel>> ListRoles(int executingUser, string domainName)
|
||||
{ return QueryList<RoleOptionModel>("dbo.PA_Sis_RolList", executingUser, domainName, null); }
|
||||
|
||||
public DbResult<List<StateOptionModel>> ListStates(string domainName)
|
||||
{ return QueryList<StateOptionModel>("dbo.PA_Gral_EstadoList", 0, domainName, null); }
|
||||
|
||||
public DbResult<int> SaveUser(UserAdministrationModel user, string passwordHash, int executingUser, string domainName)
|
||||
{
|
||||
return _db.Execute(domainName, connection =>
|
||||
{
|
||||
using (IDbCommand command = connection.CreateCommand())
|
||||
{
|
||||
StoredProcedureCommand.Configure(command, "dbo.PA_Sis_UsuarioSave", executingUser);
|
||||
StoredProcedureCommand.AddInput(command, "@CveUsuario", user.CveUsuario <= 0 ? (object)null : user.CveUsuario, DbType.Int32);
|
||||
StoredProcedureCommand.AddInput(command, "@NombreUsuario", user.NombreUsuario, DbType.String);
|
||||
StoredProcedureCommand.AddInput(command, "@CorreoElectronico", user.CorreoElectronico, DbType.String);
|
||||
StoredProcedureCommand.AddInput(command, "@CveRol", user.CveRol, DbType.Int32);
|
||||
StoredProcedureCommand.AddInput(command, "@HashContrasena", passwordHash, DbType.String);
|
||||
StoredProcedureCommand.AddInput(command, "@Activo", user.Activo, DbType.Boolean);
|
||||
StoredProcedureCommand.AddInput(command, "@DebeCambiarContrasena", user.DebeCambiarContrasena, DbType.Boolean);
|
||||
int id;
|
||||
using (IDataReader reader = command.ExecuteReader()) { id = reader.Read() ? reader.GetInt32(0) : 0; }
|
||||
StoredProcedureCommand.EnsureSuccess(command);
|
||||
return id;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public DbResult<bool> DeleteUser(int userId, int executingUser, string domainName)
|
||||
{ return Execute("dbo.PA_Sis_UsuarioDelete", executingUser, domainName, c => StoredProcedureCommand.AddInput(c, "@CveUsuario", userId, DbType.Int32)); }
|
||||
|
||||
public DbResult<List<UserClientModel>> ListUserClients(int userId, int executingUser, string domainName)
|
||||
{ return QueryList<UserClientModel>("dbo.PA_Sis_UsuarioClienteList", executingUser, domainName, c => StoredProcedureCommand.AddInput(c, "@CveUsuario", userId, DbType.Int32)); }
|
||||
|
||||
public DbResult<bool> SaveUserClient(int userId, int clientId, int order, bool primary, int executingUser, string domainName)
|
||||
{
|
||||
return Execute("dbo.PA_Sis_UsuarioClienteSave", executingUser, domainName, c =>
|
||||
{
|
||||
StoredProcedureCommand.AddInput(c, "@CveUsuario", userId, DbType.Int32);
|
||||
StoredProcedureCommand.AddInput(c, "@CveCliente", clientId, DbType.Int32);
|
||||
StoredProcedureCommand.AddInput(c, "@Orden", order, DbType.Int32);
|
||||
StoredProcedureCommand.AddInput(c, "@EsPrincipal", primary, DbType.Boolean);
|
||||
});
|
||||
}
|
||||
|
||||
public DbResult<bool> ValidateRegistration(string username, string email, string rfc, string domainName)
|
||||
{
|
||||
return Execute("dbo.PA_Sis_RegistroClienteValidate", 0, domainName, c =>
|
||||
{
|
||||
StoredProcedureCommand.AddInput(c, "@NombreUsuario", username, DbType.String);
|
||||
StoredProcedureCommand.AddInput(c, "@CorreoElectronico", email, DbType.String);
|
||||
StoredProcedureCommand.AddInput(c, "@RFC", rfc, DbType.String);
|
||||
});
|
||||
}
|
||||
|
||||
public DbResult<RegistrationCreatedModel> CreateRegistration(PublicRegistrationDraft draft, string passwordHash, string tokenHash, string domainName)
|
||||
{
|
||||
return QueryOne<RegistrationCreatedModel>("dbo.PA_Sis_RegistroClienteAdd", 0, domainName, c =>
|
||||
{
|
||||
StoredProcedureCommand.AddInput(c, "@NombreUsuario", draft.NombreUsuario, DbType.String);
|
||||
StoredProcedureCommand.AddInput(c, "@HashContrasena", passwordHash, DbType.String);
|
||||
StoredProcedureCommand.AddInput(c, "@CorreoElectronico", draft.CorreoElectronico, DbType.String);
|
||||
StoredProcedureCommand.AddInput(c, "@RFC", draft.RFC, DbType.String);
|
||||
StoredProcedureCommand.AddInput(c, "@RazonSocial", draft.RazonSocial, DbType.String);
|
||||
StoredProcedureCommand.AddInput(c, "@NombreComercial", draft.NombreComercial, DbType.String);
|
||||
StoredProcedureCommand.AddInput(c, "@Direccion", draft.Direccion, DbType.String);
|
||||
StoredProcedureCommand.AddInput(c, "@NoExt", draft.NoExt, DbType.String);
|
||||
StoredProcedureCommand.AddInput(c, "@NoInt", draft.NoInt, DbType.String);
|
||||
StoredProcedureCommand.AddInput(c, "@Colonia", draft.Colonia, DbType.String);
|
||||
StoredProcedureCommand.AddInput(c, "@Ciudad", draft.Ciudad, DbType.String);
|
||||
StoredProcedureCommand.AddInput(c, "@CveSATEstado", draft.CveSATEstado, DbType.Int32);
|
||||
StoredProcedureCommand.AddInput(c, "@Pais", draft.Pais, DbType.String);
|
||||
StoredProcedureCommand.AddInput(c, "@CP", draft.CP, DbType.String);
|
||||
StoredProcedureCommand.AddInput(c, "@Telefono", draft.Telefono, DbType.String);
|
||||
StoredProcedureCommand.AddInput(c, "@HashToken", tokenHash, DbType.StringFixedLength);
|
||||
StoredProcedureCommand.AddInput(c, "@HorasExpiracion", 24, DbType.Int32);
|
||||
});
|
||||
}
|
||||
|
||||
public DbResult<bool> VerifyEmail(string tokenHash, string domainName)
|
||||
{ return Execute("dbo.PA_Sis_CorreoVerify", 0, domainName, c => StoredProcedureCommand.AddInput(c, "@HashToken", tokenHash, DbType.StringFixedLength)); }
|
||||
|
||||
private DbResult<List<T>> QueryList<T>(string procedure, int user, string domain, System.Action<IDbCommand> parameters) where T : class, new()
|
||||
{
|
||||
return _db.Execute(domain, connection =>
|
||||
{
|
||||
using (IDbCommand command = connection.CreateCommand())
|
||||
{
|
||||
StoredProcedureCommand.Configure(command, procedure, user); if (parameters != null) parameters(command);
|
||||
List<T> value; using (IDataReader reader = command.ExecuteReader()) { value = reader.ToList<T>(); }
|
||||
StoredProcedureCommand.EnsureSuccess(command); return value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private DbResult<T> QueryOne<T>(string procedure, int user, string domain, System.Action<IDbCommand> parameters) where T : class, new()
|
||||
{
|
||||
return _db.Execute(domain, connection =>
|
||||
{
|
||||
using (IDbCommand command = connection.CreateCommand())
|
||||
{
|
||||
StoredProcedureCommand.Configure(command, procedure, user); if (parameters != null) parameters(command);
|
||||
T value; using (IDataReader reader = command.ExecuteReader()) { value = reader.ToObject<T>(); }
|
||||
StoredProcedureCommand.EnsureSuccess(command); return value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private DbResult<bool> Execute(string procedure, int user, string domain, System.Action<IDbCommand> parameters)
|
||||
{
|
||||
return _db.Execute(domain, connection =>
|
||||
{
|
||||
using (IDbCommand command = connection.CreateCommand())
|
||||
{
|
||||
StoredProcedureCommand.Configure(command, procedure, user); if (parameters != null) parameters(command);
|
||||
command.ExecuteNonQuery(); StoredProcedureCommand.EnsureSuccess(command); return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
68
SIG.Web/AppCode/Repositories/Security/UserRepository.cs
Normal file
68
SIG.Web/AppCode/Repositories/Security/UserRepository.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using SIG.Web.AppCode.Contracts.Database;
|
||||
using SIG.Web.AppCode.Contracts.Security;
|
||||
using SIG.Web.AppCode.Models.Common;
|
||||
using SIG.Web.AppCode.Models.Security;
|
||||
using SIG.Web.AppCode.Services.Database;
|
||||
using SIG.Web.AppCode.Utilities;
|
||||
|
||||
namespace SIG.Web.AppCode.Repositories.Security
|
||||
{
|
||||
public sealed class UserRepository : IUserRepository
|
||||
{
|
||||
private readonly IDbExecutor _dbExecutor;
|
||||
|
||||
public UserRepository(IDbExecutor dbExecutor)
|
||||
{
|
||||
_dbExecutor = dbExecutor;
|
||||
}
|
||||
|
||||
public DbResult<UserModel> ObtenerPorNombreUsuario(string username, string domainName)
|
||||
{
|
||||
return _dbExecutor.Execute(domainName, connection =>
|
||||
{
|
||||
using (IDbCommand command = connection.CreateCommand())
|
||||
{
|
||||
StoredProcedureCommand.Configure(command, "dbo.PA_Sis_UsuarioGetByName", 0);
|
||||
StoredProcedureCommand.AddInput(command, "@NombreUsuario", username, DbType.String);
|
||||
|
||||
UserModel user;
|
||||
|
||||
using (IDataReader reader = command.ExecuteReader())
|
||||
{
|
||||
user = reader.ToObject<UserModel>();
|
||||
}
|
||||
|
||||
StoredProcedureCommand.EnsureSuccess(command);
|
||||
return user;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public DbResult<bool> RegisterSuccessfulLogin(int cveUser, string domainName)
|
||||
{
|
||||
return ExecuteUserUpdate("dbo.PA_Sis_UsuarioLoginSuccessUpdate", cveUser, domainName);
|
||||
}
|
||||
|
||||
public DbResult<bool> RegisterFailedLogin(int cveUser, string domainName)
|
||||
{
|
||||
return ExecuteUserUpdate("dbo.PA_Sis_UsuarioLoginFailedUpdate", cveUser, domainName);
|
||||
}
|
||||
|
||||
private DbResult<bool> ExecuteUserUpdate(string procedureName, int cveUser, string domainName)
|
||||
{
|
||||
return _dbExecutor.Execute(domainName, connection =>
|
||||
{
|
||||
using (IDbCommand command = connection.CreateCommand())
|
||||
{
|
||||
StoredProcedureCommand.Configure(command, procedureName, cveUser);
|
||||
StoredProcedureCommand.AddInput(command, "@CveUsuario", cveUser, DbType.Int32);
|
||||
command.ExecuteNonQuery();
|
||||
StoredProcedureCommand.EnsureSuccess(command);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.Data;
|
||||
using SIG.Web.AppCode.Contracts.Database;
|
||||
using SIG.Web.AppCode.Contracts.Security;
|
||||
using SIG.Web.AppCode.Models.Common;
|
||||
using SIG.Web.AppCode.Services.Database;
|
||||
|
||||
namespace SIG.Web.AppCode.Repositories.Security
|
||||
{
|
||||
public sealed class UserSessionRepository : IUserSessionRepository
|
||||
{
|
||||
private readonly IDbExecutor _dbExecutor;
|
||||
|
||||
public UserSessionRepository(IDbExecutor dbExecutor)
|
||||
{
|
||||
_dbExecutor = dbExecutor;
|
||||
}
|
||||
|
||||
public DbResult<bool> StartSession(int cveUser, string sessionToken, string ipAddress, string userAgent, string domainName)
|
||||
{
|
||||
return _dbExecutor.Execute(domainName, connection =>
|
||||
{
|
||||
using (IDbCommand command = connection.CreateCommand())
|
||||
{
|
||||
StoredProcedureCommand.Configure(command, "dbo.PA_Sis_UsuarioSesionAdd", cveUser);
|
||||
StoredProcedureCommand.AddInput(command, "@CveUsuario", cveUser, DbType.Int32);
|
||||
StoredProcedureCommand.AddInput(command, "@TokenSesion", sessionToken, DbType.String);
|
||||
StoredProcedureCommand.AddInput(command, "@DireccionIP", ipAddress, DbType.String);
|
||||
StoredProcedureCommand.AddInput(command, "@AgenteUsuario", userAgent, DbType.String);
|
||||
StoredProcedureCommand.AddInput(command, "@NombreDominio", domainName, DbType.String);
|
||||
|
||||
command.ExecuteNonQuery();
|
||||
StoredProcedureCommand.EnsureSuccess(command);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public DbResult<bool> EndSession(string sessionToken, string domainName)
|
||||
{
|
||||
return _dbExecutor.Execute(domainName, connection =>
|
||||
{
|
||||
using (IDbCommand command = connection.CreateCommand())
|
||||
{
|
||||
StoredProcedureCommand.Configure(command, "dbo.PA_Sis_UsuarioSesionEnd", 0);
|
||||
StoredProcedureCommand.AddInput(command, "@TokenSesion", sessionToken, DbType.String);
|
||||
|
||||
command.ExecuteNonQuery();
|
||||
StoredProcedureCommand.EnsureSuccess(command);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
109
SIG.Web/AppCode/Security/ClsPaginaBase.cs
Normal file
109
SIG.Web/AppCode/Security/ClsPaginaBase.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
using SIG.Web.AppCode.Configuration;
|
||||
using SIG.Web.AppCode.Services.Logging;
|
||||
using SIG.Web.AppCode.Session;
|
||||
using System;
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
|
||||
namespace SIG.Web.AppCode.Security
|
||||
{
|
||||
public abstract class ClsPaginaBase : Page
|
||||
{
|
||||
protected UserSessionModel CurrentUser { get; private set; }
|
||||
|
||||
protected override void OnInit(EventArgs e)
|
||||
{
|
||||
base.OnInit(e);
|
||||
|
||||
string currentPath = Request.AppRelativeCurrentExecutionFilePath;
|
||||
|
||||
if (IsPublicPage(currentPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentUser = AppServices.UserContext.GetCurrentUser();
|
||||
|
||||
if (CurrentUser == null)
|
||||
{
|
||||
Response.Redirect(BuildLoginUrl(currentPath), false);
|
||||
Context.ApplicationInstance.CompleteRequest();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsHomePage(currentPath) &&
|
||||
!AppServices.PermissionService.CanAccessPage(CurrentUser.CveUsuario, currentPath))
|
||||
{
|
||||
Response.Redirect("~/AccessDenied.aspx", false);
|
||||
Context.ApplicationInstance.CompleteRequest();
|
||||
}
|
||||
}
|
||||
|
||||
private string BuildLoginUrl(string currentPath)
|
||||
{
|
||||
string loginUrl = ResolveUrl("~/Login.aspx");
|
||||
|
||||
if (IsHomePage(currentPath))
|
||||
{
|
||||
return loginUrl;
|
||||
}
|
||||
|
||||
string retornoURL =
|
||||
Request.Url == null
|
||||
? null
|
||||
: Request.Url.PathAndQuery;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(retornoURL) ||
|
||||
retornoURL.Equals("/", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return loginUrl;
|
||||
}
|
||||
|
||||
return loginUrl +
|
||||
"?retornoURL=" +
|
||||
HttpUtility.UrlEncode(retornoURL);
|
||||
}
|
||||
|
||||
private static bool IsHomePage(string appRelativePath)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(appRelativePath) ||
|
||||
appRelativePath.Equals("~/Inicio.aspx", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static bool IsPublicPage(string appRelativePath)
|
||||
{
|
||||
return appRelativePath != null &&
|
||||
(appRelativePath.Equals("~/Login.aspx", StringComparison.OrdinalIgnoreCase) ||
|
||||
appRelativePath.Equals("~/Logout.aspx", StringComparison.OrdinalIgnoreCase) ||
|
||||
appRelativePath.Equals("~/AccessDenied.aspx", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
protected override void OnError(EventArgs e)
|
||||
{
|
||||
Exception exception = Server.GetLastError();
|
||||
|
||||
if (exception == null)
|
||||
{
|
||||
base.OnError(e);
|
||||
return;
|
||||
}
|
||||
|
||||
ExceptionLogger.Log(
|
||||
exception,
|
||||
GetType().FullName
|
||||
);
|
||||
|
||||
string message =
|
||||
ErrorHandlerConfig.GetFriendlyMessage(exception);
|
||||
|
||||
Server.ClearError();
|
||||
|
||||
Site siteMaster = Master as Site;
|
||||
|
||||
if (siteMaster != null)
|
||||
{
|
||||
siteMaster.ShowGlobalError(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
SIG.Web/AppCode/Security/DummyPermissionService.cs
Normal file
12
SIG.Web/AppCode/Security/DummyPermissionService.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using SIG.Web.AppCode.Session;
|
||||
|
||||
namespace SIG.Web.AppCode.Security
|
||||
{
|
||||
public sealed class DummyPermissionService : IPermissionService
|
||||
{
|
||||
public bool CanAccess(UserSessionModel user, string appRelativePath)
|
||||
{
|
||||
return user != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
9
SIG.Web/AppCode/Security/IPermissionService.cs
Normal file
9
SIG.Web/AppCode/Security/IPermissionService.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using SIG.Web.AppCode.Session;
|
||||
|
||||
namespace SIG.Web.AppCode.Security
|
||||
{
|
||||
public interface IPermissionService
|
||||
{
|
||||
bool CanAccess(UserSessionModel user, string appRelativePath);
|
||||
}
|
||||
}
|
||||
37
SIG.Web/AppCode/Services/Data/DbConnectionFactory.cs
Normal file
37
SIG.Web/AppCode/Services/Data/DbConnectionFactory.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Npgsql;
|
||||
using SIG.Web.AppCode.Contracts.Data;
|
||||
|
||||
namespace SIG.Web.AppCode.Services.Data
|
||||
{
|
||||
public sealed class DbConnectionFactory : IDbConnectionFactory
|
||||
{
|
||||
private readonly IDomainConnectionResolver _resolver;
|
||||
|
||||
public DbConnectionFactory(IDomainConnectionResolver resolver)
|
||||
{
|
||||
_resolver = resolver;
|
||||
}
|
||||
|
||||
public IDbConnection Create(string domain)
|
||||
{
|
||||
var settings = _resolver.Resolve(domain);
|
||||
var connection = ConfigurationManager.ConnectionStrings[settings.ConnectionStringName];
|
||||
if (connection == null)
|
||||
throw new InvalidOperationException("Connection string not found: " + settings.ConnectionStringName);
|
||||
|
||||
switch (settings.Provider)
|
||||
{
|
||||
case DbProviderKind.SqlServer:
|
||||
return new SqlConnection(connection.ConnectionString);
|
||||
case DbProviderKind.PostgreSql:
|
||||
return new NpgsqlConnection(connection.ConnectionString);
|
||||
default:
|
||||
throw new NotSupportedException("Unsupported database provider: " + settings.Provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
28
SIG.Web/AppCode/Services/Data/DomainConnectionResolver.cs
Normal file
28
SIG.Web/AppCode/Services/Data/DomainConnectionResolver.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using SIG.Web.AppCode.Contracts.Data;
|
||||
|
||||
namespace SIG.Web.AppCode.Services.Data
|
||||
{
|
||||
public sealed class DomainConnectionResolver : IDomainConnectionResolver
|
||||
{
|
||||
public DomainConnectionSettings Resolve(string domain)
|
||||
{
|
||||
// Default first. Later this can read a table or config mapping by host/domain.
|
||||
var providerText = ConfigurationManager.AppSettings["DefaultDbProvider"] ?? "SqlServer";
|
||||
var connectionName = ConfigurationManager.AppSettings["DefaultConnectionStringName"] ?? "SIG_DefaultSql";
|
||||
|
||||
DbProviderKind provider;
|
||||
if (!Enum.TryParse(providerText, true, out provider))
|
||||
provider = DbProviderKind.SqlServer;
|
||||
|
||||
return new DomainConnectionSettings
|
||||
{
|
||||
Domain = domain,
|
||||
Provider = provider,
|
||||
ConnectionStringName = connectionName,
|
||||
IsDefault = true
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
19
SIG.Web/AppCode/Services/Database/DbConnectionFactory.cs
Normal file
19
SIG.Web/AppCode/Services/Database/DbConnectionFactory.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
using SIG.Web.AppCode.Contracts.Database;
|
||||
namespace SIG.Web.AppCode.Services.Database
|
||||
{
|
||||
public sealed class DbConnectionFactory : IDbConnectionFactory
|
||||
{
|
||||
public IDbConnection CreateConnection(string domainName)
|
||||
{
|
||||
string name = ConfigurationManager.AppSettings["DefaultConnectionStringName"];
|
||||
if (string.IsNullOrWhiteSpace(name)) name = "SIG_DefaultSql";
|
||||
ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings[name];
|
||||
if (settings == null) throw new InvalidOperationException("Connection string not found: " + name);
|
||||
return new SqlConnection(settings.ConnectionString);
|
||||
}
|
||||
}
|
||||
}
|
||||
162
SIG.Web/AppCode/Services/Database/DbExecutor.cs
Normal file
162
SIG.Web/AppCode/Services/Database/DbExecutor.cs
Normal file
@@ -0,0 +1,162 @@
|
||||
using SIG.Web.AppCode.Contracts.Database;
|
||||
using SIG.Web.AppCode.Models.Common;
|
||||
using SIG.Web.AppCode.Services.Logging;
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
|
||||
namespace SIG.Web.AppCode.Services.Database
|
||||
{
|
||||
public sealed class DbExecutor : IDbExecutor
|
||||
{
|
||||
private readonly IDbConnectionFactory _connectionFactory;
|
||||
|
||||
public DbExecutor(IDbConnectionFactory connectionFactory)
|
||||
{
|
||||
_connectionFactory = connectionFactory;
|
||||
}
|
||||
|
||||
public DbResult<T> Execute<T>(string domainName, Func<IDbConnection, T> action)
|
||||
{
|
||||
IDbConnection connection = null;
|
||||
SqlConnection sqlConnection = null;
|
||||
SqlInfoMessageEventHandler infoMessageHandler = null;
|
||||
|
||||
try
|
||||
{
|
||||
if (action == null)
|
||||
{
|
||||
throw new ArgumentNullException("action");
|
||||
}
|
||||
|
||||
connection = _connectionFactory.CreateConnection(domainName);
|
||||
sqlConnection = connection as SqlConnection;
|
||||
|
||||
if (sqlConnection != null)
|
||||
{
|
||||
/*
|
||||
* No activar FireInfoMessageEventOnUserErrors: al hacerlo, SQL Server
|
||||
* degrada errores de severidad 11-16 a InfoMessage y evita el catch.
|
||||
* Los RAISERROR informativos de severidad 10 llegan al evento sin esa opción.
|
||||
*/
|
||||
infoMessageHandler = delegate(object sender, SqlInfoMessageEventArgs eventArgs)
|
||||
{
|
||||
foreach (SqlError error in eventArgs.Errors)
|
||||
{
|
||||
ExceptionLogger.Log(
|
||||
CreateDiagnosticException(new InvalidOperationException(error.Message)),
|
||||
"DbExecutor.SqlInfoMessage");
|
||||
}
|
||||
};
|
||||
sqlConnection.InfoMessage += infoMessageHandler;
|
||||
}
|
||||
|
||||
if (connection.State != ConnectionState.Open)
|
||||
{
|
||||
connection.Open();
|
||||
}
|
||||
|
||||
T data = action(connection);
|
||||
return DbResult<T>.Ok(data);
|
||||
}
|
||||
catch (StoredProcedureException ex)
|
||||
{
|
||||
ExceptionLogger.Log(CreateDiagnosticException(ex), "DbExecutor.StoredProcedure", ex.UserMessage);
|
||||
return DbResult<T>.Fail(ex.UserMessage, BuildTechnicalMessage(ex), ex);
|
||||
}
|
||||
catch (SqlException ex)
|
||||
{
|
||||
string userMessage;
|
||||
|
||||
switch (ex.Number)
|
||||
{
|
||||
case 208:
|
||||
userMessage = "Falta una tabla, vista o procedimiento requerido en la base de datos.";
|
||||
break;
|
||||
|
||||
case 207:
|
||||
userMessage = "Falta una columna requerida en la base de datos.";
|
||||
break;
|
||||
|
||||
case 2812:
|
||||
userMessage = "Falta un procedimiento requerido en la base de datos.";
|
||||
break;
|
||||
|
||||
case 4060:
|
||||
case 18456:
|
||||
userMessage = "No fue posible abrir la base de datos configurada.";
|
||||
break;
|
||||
|
||||
case -2:
|
||||
userMessage = "La base de datos tardó demasiado en responder.";
|
||||
break;
|
||||
|
||||
default:
|
||||
userMessage = "Ocurrió un error al consultar la base de datos.";
|
||||
break;
|
||||
}
|
||||
|
||||
ExceptionLogger.Log(CreateDiagnosticException(ex), "DbExecutor.SqlException", userMessage);
|
||||
return DbResult<T>.Fail(userMessage, BuildTechnicalMessage(ex), ex);
|
||||
}
|
||||
catch (TimeoutException ex)
|
||||
{
|
||||
const string userMessage = "La base de datos tardó demasiado en responder. Intente nuevamente.";
|
||||
ExceptionLogger.Log(CreateDiagnosticException(ex), "DbExecutor.TimeoutException", userMessage);
|
||||
return DbResult<T>.Fail(userMessage, BuildTechnicalMessage(ex), ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
const string userMessage = "Ocurrió un error al consultar la base de datos.";
|
||||
ExceptionLogger.Log(CreateDiagnosticException(ex), "DbExecutor.Exception", userMessage);
|
||||
return DbResult<T>.Fail(userMessage, BuildTechnicalMessage(ex), ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (sqlConnection != null && infoMessageHandler != null)
|
||||
{
|
||||
sqlConnection.InfoMessage -= infoMessageHandler;
|
||||
}
|
||||
|
||||
if (connection != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (connection.State != ConnectionState.Closed)
|
||||
{
|
||||
connection.Close();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
connection.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
StoredProcedureDiagnostics.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static Exception CreateDiagnosticException(Exception exception)
|
||||
{
|
||||
string diagnostic = StoredProcedureDiagnostics.BuildDiagnostic();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(diagnostic))
|
||||
{
|
||||
return exception;
|
||||
}
|
||||
|
||||
return new InvalidOperationException(
|
||||
exception.Message + Environment.NewLine + diagnostic,
|
||||
exception);
|
||||
}
|
||||
|
||||
private static string BuildTechnicalMessage(Exception exception)
|
||||
{
|
||||
string diagnostic = StoredProcedureDiagnostics.BuildDiagnostic();
|
||||
return string.IsNullOrWhiteSpace(diagnostic)
|
||||
? exception.Message
|
||||
: exception.Message + Environment.NewLine + diagnostic;
|
||||
}
|
||||
}
|
||||
}
|
||||
133
SIG.Web/AppCode/Services/Database/StoredProcedureCommand.cs
Normal file
133
SIG.Web/AppCode/Services/Database/StoredProcedureCommand.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using SIG.Web.AppCode.Models.Common;
|
||||
|
||||
namespace SIG.Web.AppCode.Services.Database
|
||||
{
|
||||
public static class StoredProcedureCommand
|
||||
{
|
||||
public static void Configure(IDbCommand command, string procedureName, int cveUsuarioEjecuta)
|
||||
{
|
||||
if (command == null)
|
||||
{
|
||||
throw new ArgumentNullException("command");
|
||||
}
|
||||
|
||||
StoredProcedureDiagnostics.Start(procedureName);
|
||||
command.CommandText = procedureName;
|
||||
command.CommandType = CommandType.StoredProcedure;
|
||||
|
||||
AddInput(command, "@CveUsuarioEjecuta", cveUsuarioEjecuta, DbType.Int32);
|
||||
AddOutput(command, "@ErrorMostrar", DbType.String, -1);
|
||||
AddOutput(command, "@ErrorTipo", DbType.Int32, 0);
|
||||
AddOutput(command, "@Return", DbType.Boolean, 0);
|
||||
}
|
||||
|
||||
public static IDbDataParameter AddInput(IDbCommand command, string name, object value)
|
||||
{
|
||||
return AddInput(command, name, value, null);
|
||||
}
|
||||
|
||||
public static IDbDataParameter AddInput(IDbCommand command, string name, object value, DbType? dbType)
|
||||
{
|
||||
IDbDataParameter parameter = command.CreateParameter();
|
||||
parameter.ParameterName = name;
|
||||
parameter.Direction = ParameterDirection.Input;
|
||||
parameter.Value = value ?? DBNull.Value;
|
||||
|
||||
if (dbType.HasValue)
|
||||
{
|
||||
parameter.DbType = dbType.Value;
|
||||
}
|
||||
|
||||
command.Parameters.Add(parameter);
|
||||
StoredProcedureDiagnostics.AddParameter(
|
||||
parameter.ParameterName,
|
||||
parameter.Value,
|
||||
parameter.DbType,
|
||||
parameter.Direction);
|
||||
|
||||
return parameter;
|
||||
}
|
||||
|
||||
public static StoredProcedureResult ReadResult(IDbCommand command)
|
||||
{
|
||||
StoredProcedureDiagnostics.CaptureOutputs(command);
|
||||
|
||||
return new StoredProcedureResult
|
||||
{
|
||||
Return = ReadBoolean(command, "@Return"),
|
||||
ErrorTipo = ReadInteger(command, "@ErrorTipo"),
|
||||
ErrorMostrar = ReadString(command, "@ErrorMostrar")
|
||||
};
|
||||
}
|
||||
|
||||
public static void EnsureSuccess(IDbCommand command)
|
||||
{
|
||||
StoredProcedureResult result = ReadResult(command);
|
||||
|
||||
if (result.Return)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
object returnValue = ((IDataParameter)command.Parameters["@Return"]).Value;
|
||||
bool outputsReceived = returnValue != null && returnValue != DBNull.Value;
|
||||
|
||||
string message;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(result.ErrorMostrar))
|
||||
{
|
||||
message = result.ErrorMostrar;
|
||||
}
|
||||
else if (!outputsReceived)
|
||||
{
|
||||
message = "La versión instalada del procedimiento no devolvió los parámetros requeridos; favor de contactar a Sistemas.";
|
||||
}
|
||||
else
|
||||
{
|
||||
message = "La operación no pudo completarse.";
|
||||
}
|
||||
|
||||
throw new StoredProcedureException(message, result.ErrorTipo);
|
||||
}
|
||||
|
||||
private static void AddOutput(IDbCommand command, string name, DbType dbType, int size)
|
||||
{
|
||||
IDbDataParameter parameter = command.CreateParameter();
|
||||
parameter.ParameterName = name;
|
||||
parameter.Direction = ParameterDirection.Output;
|
||||
parameter.DbType = dbType;
|
||||
|
||||
if (size != 0)
|
||||
{
|
||||
parameter.Size = size;
|
||||
}
|
||||
|
||||
command.Parameters.Add(parameter);
|
||||
StoredProcedureDiagnostics.AddParameter(
|
||||
parameter.ParameterName,
|
||||
parameter.Value,
|
||||
parameter.DbType,
|
||||
parameter.Direction);
|
||||
}
|
||||
|
||||
private static bool ReadBoolean(IDbCommand command, string name)
|
||||
{
|
||||
object value = ((IDataParameter)command.Parameters[name]).Value;
|
||||
return value != null && value != DBNull.Value && Convert.ToBoolean(value);
|
||||
}
|
||||
|
||||
private static int ReadInteger(IDbCommand command, string name)
|
||||
{
|
||||
object value = ((IDataParameter)command.Parameters[name]).Value;
|
||||
return value == null || value == DBNull.Value ? 0 : Convert.ToInt32(value);
|
||||
}
|
||||
|
||||
private static string ReadString(IDbCommand command, string name)
|
||||
{
|
||||
object value = ((IDataParameter)command.Parameters[name]).Value;
|
||||
return value == null || value == DBNull.Value ? string.Empty : Convert.ToString(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
276
SIG.Web/AppCode/Services/Database/StoredProcedureDiagnostics.cs
Normal file
276
SIG.Web/AppCode/Services/Database/StoredProcedureDiagnostics.cs
Normal file
@@ -0,0 +1,276 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace SIG.Web.AppCode.Services.Database
|
||||
{
|
||||
public static class StoredProcedureDiagnostics
|
||||
{
|
||||
private static readonly AsyncLocal<DiagnosticContext> CurrentContext =
|
||||
new AsyncLocal<DiagnosticContext>();
|
||||
|
||||
public static void Start(string procedureName)
|
||||
{
|
||||
CurrentContext.Value = new DiagnosticContext
|
||||
{
|
||||
ProcedureName = procedureName
|
||||
};
|
||||
}
|
||||
|
||||
public static void AddParameter(string name, object value, DbType dbType, ParameterDirection direction)
|
||||
{
|
||||
DiagnosticContext context = CurrentContext.Value;
|
||||
|
||||
if (context == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
context.Parameters.Add(new DiagnosticParameter
|
||||
{
|
||||
Name = name,
|
||||
Value = value,
|
||||
DbType = dbType,
|
||||
Direction = direction
|
||||
});
|
||||
}
|
||||
|
||||
public static void CaptureOutputs(IDbCommand command)
|
||||
{
|
||||
DiagnosticContext context = CurrentContext.Value;
|
||||
|
||||
if (context == null || command == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (IDataParameter commandParameter in command.Parameters)
|
||||
{
|
||||
if (commandParameter.Direction == ParameterDirection.Input)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
DiagnosticParameter parameter = context.Parameters.FirstOrDefault(
|
||||
item => item.Name.Equals(commandParameter.ParameterName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (parameter != null)
|
||||
{
|
||||
parameter.Value = commandParameter.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string BuildDiagnostic()
|
||||
{
|
||||
DiagnosticContext context = CurrentContext.Value;
|
||||
|
||||
if (context == null || string.IsNullOrWhiteSpace(context.ProcedureName))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var result = new StringBuilder();
|
||||
result.AppendLine("StoredProcedure: " + context.ProcedureName);
|
||||
result.AppendLine("Parametros:");
|
||||
|
||||
foreach (DiagnosticParameter parameter in context.Parameters)
|
||||
{
|
||||
result.AppendLine(parameter.Name + "=" + FormatSqlValue(parameter));
|
||||
}
|
||||
|
||||
result.AppendLine("ReplaySql:");
|
||||
AppendReplaySql(result, context);
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
public static void Clear()
|
||||
{
|
||||
CurrentContext.Value = null;
|
||||
}
|
||||
|
||||
private static void AppendReplaySql(StringBuilder result, DiagnosticContext context)
|
||||
{
|
||||
IList<DiagnosticParameter> outputs = context.Parameters
|
||||
.Where(parameter => parameter.Direction != ParameterDirection.Input)
|
||||
.ToList();
|
||||
|
||||
foreach (DiagnosticParameter output in outputs)
|
||||
{
|
||||
result.Append("DECLARE ")
|
||||
.Append(output.Name)
|
||||
.Append(' ')
|
||||
.Append(GetSqlType(output.DbType))
|
||||
.AppendLine(";");
|
||||
}
|
||||
|
||||
result.Append("EXEC ").Append(QuoteProcedureName(context.ProcedureName));
|
||||
|
||||
for (int index = 0; index < context.Parameters.Count; index++)
|
||||
{
|
||||
DiagnosticParameter parameter = context.Parameters[index];
|
||||
result.Append(index == 0 ? Environment.NewLine + " " : "," + Environment.NewLine + " ");
|
||||
result.Append(parameter.Name).Append(" = ");
|
||||
|
||||
if (parameter.Direction == ParameterDirection.Input)
|
||||
{
|
||||
result.Append(FormatSqlValue(parameter));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Append(parameter.Name).Append(" OUTPUT");
|
||||
}
|
||||
}
|
||||
|
||||
result.AppendLine(";");
|
||||
|
||||
if (outputs.Count > 0)
|
||||
{
|
||||
result.Append("SELECT ");
|
||||
|
||||
for (int index = 0; index < outputs.Count; index++)
|
||||
{
|
||||
if (index > 0)
|
||||
{
|
||||
result.Append(", ");
|
||||
}
|
||||
|
||||
result.Append(outputs[index].Name);
|
||||
}
|
||||
|
||||
result.AppendLine(";");
|
||||
}
|
||||
}
|
||||
|
||||
private static string FormatSqlValue(DiagnosticParameter parameter)
|
||||
{
|
||||
if (IsSensitive(parameter.Name))
|
||||
{
|
||||
return "N'[REDACTADO]'";
|
||||
}
|
||||
|
||||
object value = parameter.Value;
|
||||
|
||||
if (value == null || value == DBNull.Value)
|
||||
{
|
||||
return "NULL";
|
||||
}
|
||||
|
||||
if (value is bool)
|
||||
{
|
||||
return (bool)value ? "1" : "0";
|
||||
}
|
||||
|
||||
if (value is DateTime)
|
||||
{
|
||||
return "'" + ((DateTime)value).ToString("yyyy-MM-ddTHH:mm:ss.fff", CultureInfo.InvariantCulture) + "'";
|
||||
}
|
||||
|
||||
if (value is DateTimeOffset)
|
||||
{
|
||||
return "'" + ((DateTimeOffset)value).ToString("o", CultureInfo.InvariantCulture) + "'";
|
||||
}
|
||||
|
||||
if (value is Guid)
|
||||
{
|
||||
return "'" + value + "'";
|
||||
}
|
||||
|
||||
if (value is byte[])
|
||||
{
|
||||
byte[] bytes = (byte[])value;
|
||||
int length = Math.Min(bytes.Length, 64);
|
||||
return "0x" + BitConverter.ToString(bytes, 0, length).Replace("-", string.Empty)
|
||||
+ (bytes.Length > length ? "/*TRUNCADO*/" : string.Empty);
|
||||
}
|
||||
|
||||
if (value is string || value is char)
|
||||
{
|
||||
string text = Convert.ToString(value, CultureInfo.InvariantCulture) ?? string.Empty;
|
||||
|
||||
if (text.Length > 1000)
|
||||
{
|
||||
text = text.Substring(0, 1000) + " [TRUNCADO]";
|
||||
}
|
||||
|
||||
return "N'" + text.Replace("'", "''") + "'";
|
||||
}
|
||||
|
||||
if (value is float || value is double || value is decimal ||
|
||||
value is byte || value is sbyte || value is short || value is ushort ||
|
||||
value is int || value is uint || value is long || value is ulong)
|
||||
{
|
||||
return Convert.ToString(value, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
string fallback = Convert.ToString(value, CultureInfo.InvariantCulture) ?? string.Empty;
|
||||
return "N'" + fallback.Replace("'", "''") + "'";
|
||||
}
|
||||
|
||||
private static bool IsSensitive(string parameterName)
|
||||
{
|
||||
string name = (parameterName ?? string.Empty).ToLowerInvariant();
|
||||
return name.Contains("contrasena") ||
|
||||
name.Contains("password") ||
|
||||
name.Contains("hash") ||
|
||||
name.Contains("token") ||
|
||||
name.Contains("secreto") ||
|
||||
name.Contains("secret") ||
|
||||
name.Contains("apikey") ||
|
||||
name.Contains("api_key");
|
||||
}
|
||||
|
||||
private static string GetSqlType(DbType dbType)
|
||||
{
|
||||
switch (dbType)
|
||||
{
|
||||
case DbType.Boolean: return "bit";
|
||||
case DbType.Byte: return "tinyint";
|
||||
case DbType.Int16: return "smallint";
|
||||
case DbType.Int32: return "int";
|
||||
case DbType.Int64: return "bigint";
|
||||
case DbType.Decimal: return "decimal(38, 10)";
|
||||
case DbType.Double: return "float";
|
||||
case DbType.Single: return "real";
|
||||
case DbType.Date: return "date";
|
||||
case DbType.DateTime:
|
||||
case DbType.DateTime2: return "datetime2(3)";
|
||||
case DbType.DateTimeOffset: return "datetimeoffset(7)";
|
||||
case DbType.Guid: return "uniqueidentifier";
|
||||
case DbType.Binary: return "varbinary(max)";
|
||||
case DbType.AnsiString: return "varchar(max)";
|
||||
default: return "nvarchar(max)";
|
||||
}
|
||||
}
|
||||
|
||||
private static string QuoteProcedureName(string procedureName)
|
||||
{
|
||||
return string.Join(".", (procedureName ?? string.Empty)
|
||||
.Split('.')
|
||||
.Select(part => "[" + part.Replace("]", "]]" ) + "]"));
|
||||
}
|
||||
|
||||
private sealed class DiagnosticContext
|
||||
{
|
||||
public DiagnosticContext()
|
||||
{
|
||||
Parameters = new List<DiagnosticParameter>();
|
||||
}
|
||||
|
||||
public string ProcedureName { get; set; }
|
||||
public IList<DiagnosticParameter> Parameters { get; private set; }
|
||||
}
|
||||
|
||||
private sealed class DiagnosticParameter
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public object Value { get; set; }
|
||||
public DbType DbType { get; set; }
|
||||
public ParameterDirection Direction { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace SIG.Web.AppCode.Services.Database
|
||||
{
|
||||
public sealed class StoredProcedureException : Exception
|
||||
{
|
||||
public StoredProcedureException(string userMessage, int errorType)
|
||||
: base(userMessage)
|
||||
{
|
||||
UserMessage = userMessage;
|
||||
ErrorType = errorType;
|
||||
}
|
||||
|
||||
public string UserMessage { get; private set; }
|
||||
public int ErrorType { get; private set; }
|
||||
}
|
||||
}
|
||||
720
SIG.Web/AppCode/Services/Error/SIGErrorManager.cs
Normal file
720
SIG.Web/AppCode/Services/Error/SIGErrorManager.cs
Normal file
@@ -0,0 +1,720 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using System.Web.Hosting;
|
||||
using SIG.Web.AppCode.Configuration;
|
||||
using SIG.Web.AppCode.Session;
|
||||
|
||||
namespace SIG.Web.AppCode.Services.Errors
|
||||
{
|
||||
public sealed class SIGErrorRegistro
|
||||
{
|
||||
public string Codigo { get; set; }
|
||||
public DateTime Fecha { get; set; }
|
||||
public string Origen { get; set; }
|
||||
public string CveUsuario { get; set; }
|
||||
public string NombreUsuario { get; set; }
|
||||
public string MetodoHttp { get; set; }
|
||||
public string Ruta { get; set; }
|
||||
public string Ip { get; set; }
|
||||
public string AgenteUsuario { get; set; }
|
||||
public bool EsPostbackAsincrono { get; set; }
|
||||
public string DetalleTecnico { get; set; }
|
||||
}
|
||||
|
||||
public interface ISIGErrorStore
|
||||
{
|
||||
void Guardar(SIGErrorRegistro registro);
|
||||
|
||||
void MarcarReportado(
|
||||
string codigo,
|
||||
DateTime fecha,
|
||||
string cveUsuario,
|
||||
string nombreUsuario,
|
||||
string ruta);
|
||||
}
|
||||
|
||||
public sealed class SIGTextFileErrorStore : ISIGErrorStore
|
||||
{
|
||||
private static readonly object BloqueoArchivo = new object();
|
||||
|
||||
public void Guardar(SIGErrorRegistro registro)
|
||||
{
|
||||
if (registro == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder texto = new StringBuilder();
|
||||
|
||||
texto.AppendLine();
|
||||
texto.AppendLine("============================================================");
|
||||
texto.AppendLine("ERROR");
|
||||
texto.AppendLine("============================================================");
|
||||
texto.AppendLine("Codigo: " + Safe(registro.Codigo));
|
||||
texto.AppendLine("Fecha: " + registro.Fecha.ToString("yyyy-MM-dd HH:mm:ss.fff"));
|
||||
texto.AppendLine("Origen: " + Safe(registro.Origen));
|
||||
texto.AppendLine("CveUsuario: " + Safe(registro.CveUsuario));
|
||||
texto.AppendLine("Nombre: " + Safe(registro.NombreUsuario));
|
||||
texto.AppendLine("Metodo: " + Safe(registro.MetodoHttp));
|
||||
texto.AppendLine("Ruta: " + Safe(registro.Ruta));
|
||||
texto.AppendLine("IP: " + Safe(registro.Ip));
|
||||
texto.AppendLine("AgenteUsuario: " + Safe(registro.AgenteUsuario));
|
||||
texto.AppendLine("AsyncPostBack: " + registro.EsPostbackAsincrono);
|
||||
texto.AppendLine();
|
||||
texto.AppendLine("Detalle tecnico:");
|
||||
texto.AppendLine(Safe(registro.DetalleTecnico));
|
||||
texto.AppendLine("============================================================");
|
||||
|
||||
Escribir(texto.ToString());
|
||||
}
|
||||
|
||||
public void MarcarReportado(
|
||||
string codigo,
|
||||
DateTime fecha,
|
||||
string cveUsuario,
|
||||
string nombreUsuario,
|
||||
string ruta)
|
||||
{
|
||||
StringBuilder texto = new StringBuilder();
|
||||
|
||||
texto.AppendLine();
|
||||
texto.AppendLine("------------------------------------------------------------");
|
||||
texto.AppendLine("REPORTE DEL USUARIO");
|
||||
texto.AppendLine("Codigo: " + Safe(codigo));
|
||||
texto.AppendLine("Fecha: " + fecha.ToString("yyyy-MM-dd HH:mm:ss.fff"));
|
||||
texto.AppendLine("CveUsuario: " + Safe(cveUsuario));
|
||||
texto.AppendLine("Nombre: " + Safe(nombreUsuario));
|
||||
texto.AppendLine("Ruta: " + Safe(ruta));
|
||||
texto.AppendLine("------------------------------------------------------------");
|
||||
|
||||
Escribir(texto.ToString());
|
||||
}
|
||||
|
||||
private static void Escribir(string texto)
|
||||
{
|
||||
string ruta = HostingEnvironment.MapPath("~/log/log.txt");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ruta))
|
||||
{
|
||||
ruta = Path.Combine(
|
||||
AppDomain.CurrentDomain.BaseDirectory,
|
||||
"log",
|
||||
"log.txt");
|
||||
}
|
||||
|
||||
string directorio = Path.GetDirectoryName(ruta);
|
||||
|
||||
lock (BloqueoArchivo)
|
||||
{
|
||||
if (!Directory.Exists(directorio))
|
||||
{
|
||||
Directory.CreateDirectory(directorio);
|
||||
}
|
||||
|
||||
using (FileStream stream = new FileStream(
|
||||
ruta,
|
||||
FileMode.Append,
|
||||
FileAccess.Write,
|
||||
FileShare.Read))
|
||||
using (StreamWriter writer = new StreamWriter(
|
||||
stream,
|
||||
new UTF8Encoding(false)))
|
||||
{
|
||||
writer.Write(texto);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string Safe(string value)
|
||||
{
|
||||
return string.IsNullOrEmpty(value) ? string.Empty : value;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SIGErrorManager
|
||||
{
|
||||
private const string ErrorCodeDataKey = "SIG.ErrorCode";
|
||||
|
||||
private static ISIGErrorStore _store =
|
||||
new SIGTextFileErrorStore();
|
||||
|
||||
public static ISIGErrorStore Store
|
||||
{
|
||||
get { return _store; }
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException("value");
|
||||
}
|
||||
|
||||
_store = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static string Registrar(
|
||||
Exception exception,
|
||||
HttpContext context,
|
||||
string origen,
|
||||
string cveUsuario,
|
||||
string nombreUsuario)
|
||||
{
|
||||
if (exception == null)
|
||||
{
|
||||
exception = new Exception(
|
||||
"No se recibio la excepcion original.");
|
||||
}
|
||||
|
||||
string codigoExistente =
|
||||
ObtenerCodigoExistente(exception);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(codigoExistente))
|
||||
{
|
||||
return codigoExistente;
|
||||
}
|
||||
|
||||
string codigo =
|
||||
"SIG-" +
|
||||
DateTime.Now.ToString("yyyyMMdd") +
|
||||
"-" +
|
||||
Guid.NewGuid()
|
||||
.ToString("N")
|
||||
.Substring(0, 6)
|
||||
.ToUpperInvariant();
|
||||
|
||||
AsignarCodigo(exception, codigo);
|
||||
|
||||
string metodo = string.Empty;
|
||||
string ruta = string.Empty;
|
||||
string ip = string.Empty;
|
||||
string userAgent = string.Empty;
|
||||
bool esAsync = false;
|
||||
|
||||
try
|
||||
{
|
||||
if (context != null && context.Request != null)
|
||||
{
|
||||
metodo = context.Request.HttpMethod;
|
||||
ruta = context.Request.RawUrl;
|
||||
ip = context.Request.UserHostAddress;
|
||||
userAgent = context.Request.UserAgent;
|
||||
esAsync = EsPeticionAsincrona(context);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// El contexto no debe provocar otro error.
|
||||
}
|
||||
|
||||
SIGErrorRegistro registro = new SIGErrorRegistro
|
||||
{
|
||||
Codigo = codigo,
|
||||
Fecha = DateTime.Now,
|
||||
Origen = origen,
|
||||
CveUsuario = cveUsuario,
|
||||
NombreUsuario = nombreUsuario,
|
||||
MetodoHttp = metodo,
|
||||
Ruta = ruta,
|
||||
Ip = ip,
|
||||
AgenteUsuario = userAgent,
|
||||
EsPostbackAsincrono = esAsync,
|
||||
DetalleTecnico = ObtenerDetalleTecnico(exception)
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
_store.Guardar(registro);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Nunca lanzar otro error desde el manejador global.
|
||||
}
|
||||
|
||||
return codigo;
|
||||
}
|
||||
|
||||
public static void MarcarReportado(
|
||||
string codigo,
|
||||
HttpContext context,
|
||||
string cveUsuario,
|
||||
string nombreUsuario)
|
||||
{
|
||||
string ruta = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
if (context != null && context.Request != null)
|
||||
{
|
||||
ruta = context.Request.Form["ruta"];
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ruta))
|
||||
{
|
||||
ruta = context.Request.RawUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
ruta = string.Empty;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_store.MarcarReportado(
|
||||
codigo,
|
||||
DateTime.Now,
|
||||
cveUsuario,
|
||||
nombreUsuario,
|
||||
ruta);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Nunca lanzar desde el registro de reporte.
|
||||
}
|
||||
}
|
||||
|
||||
public static void ObtenerUsuarioActual(
|
||||
out string cveUsuario,
|
||||
out string nombreUsuario,
|
||||
out int cveRole)
|
||||
{
|
||||
cveUsuario = string.Empty;
|
||||
nombreUsuario = string.Empty;
|
||||
cveRole = 0;
|
||||
|
||||
try
|
||||
{
|
||||
if (AppServices.UserContext != null)
|
||||
{
|
||||
UserSessionModel usuario =
|
||||
AppServices.UserContext.GetCurrentUser();
|
||||
|
||||
if (usuario != null)
|
||||
{
|
||||
cveUsuario =
|
||||
Convert.ToString(usuario.CveUsuario);
|
||||
|
||||
nombreUsuario =
|
||||
Convert.ToString(usuario.Nombre);
|
||||
|
||||
cveRole = usuario.CveRol;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Si Session no esta disponible, continuar con Items.
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
HttpContext context = HttpContext.Current;
|
||||
|
||||
if (context == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
object itemCveUsuario =
|
||||
context.Items["SIG.Error.CveUsuario"];
|
||||
|
||||
if (itemCveUsuario == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
cveUsuario =
|
||||
Convert.ToString(itemCveUsuario);
|
||||
|
||||
nombreUsuario =
|
||||
Convert.ToString(
|
||||
context.Items["SIG.Error.NombreUsuario"]);
|
||||
|
||||
int.TryParse(
|
||||
Convert.ToString(
|
||||
context.Items["SIG.Error.CveRol"]),
|
||||
out cveRole);
|
||||
|
||||
if (cveRole == 0)
|
||||
{
|
||||
int.TryParse(
|
||||
Convert.ToString(
|
||||
context.Items["SIG.Error.CveRol"]),
|
||||
out cveRole);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
cveUsuario = string.Empty;
|
||||
nombreUsuario = string.Empty;
|
||||
cveRole = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool EsPeticionAsincrona(
|
||||
HttpContext context)
|
||||
{
|
||||
if (context == null || context.Request == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string encabezado =
|
||||
context.Request.Headers["X-MicrosoftAjax"];
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(encabezado) &&
|
||||
encabezado.IndexOf(
|
||||
"Delta=true",
|
||||
StringComparison.OrdinalIgnoreCase) >= 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return string.Equals(
|
||||
context.Request.Form["__ASYNCPOST"],
|
||||
"true",
|
||||
StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static string CrearHtmlError(
|
||||
string codigo,
|
||||
Exception exception,
|
||||
bool mostrarDetalle,
|
||||
string cveUsuarioActual,
|
||||
string nombreUsuarioActual,
|
||||
int cveRoleActual)
|
||||
{
|
||||
string codigoSeguro =
|
||||
HttpUtility.HtmlEncode(codigo);
|
||||
|
||||
string codigoJavaScript =
|
||||
HttpUtility.JavaScriptStringEncode(codigo);
|
||||
|
||||
string cveUsuarioJavaScript =
|
||||
HttpUtility.JavaScriptStringEncode(
|
||||
cveUsuarioActual ?? string.Empty);
|
||||
|
||||
string nombreUsuarioSeguro =
|
||||
HttpUtility.HtmlEncode(
|
||||
nombreUsuarioActual ?? string.Empty);
|
||||
|
||||
string urlInicio =
|
||||
VirtualPathUtility.ToAbsolute("~/Inicio.aspx");
|
||||
|
||||
string urlReporte =
|
||||
VirtualPathUtility.ToAbsolute(
|
||||
"~/ashx/ReportarError.ashx");
|
||||
|
||||
string detalleHtml = string.Empty;
|
||||
|
||||
if (mostrarDetalle && exception != null)
|
||||
{
|
||||
detalleHtml =
|
||||
"<details class=\"sig-error-details\">" +
|
||||
"<summary>Ver detalle tecnico</summary>" +
|
||||
"<pre>" +
|
||||
HttpUtility.HtmlEncode(
|
||||
ObtenerDetalleTecnico(exception)) +
|
||||
"</pre>" +
|
||||
"</details>";
|
||||
}
|
||||
|
||||
StringBuilder html = new StringBuilder();
|
||||
|
||||
html.AppendLine("<!DOCTYPE html>");
|
||||
html.AppendLine("<html lang=\"es\">");
|
||||
html.AppendLine("<head>");
|
||||
html.AppendLine(" <meta charset=\"utf-8\">");
|
||||
html.AppendLine(" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
|
||||
html.AppendLine(" <title>SIG - Error inesperado</title>");
|
||||
html.AppendLine(" <link rel=\"shortcut icon\" href=\"/Imagenes/favicon.ico\">");
|
||||
html.AppendLine(" <style>");
|
||||
html.AppendLine(" html,body{margin:0;min-height:100%;font-family:'Open Sans',Arial,sans-serif;background:#eef1f5;color:#333}");
|
||||
html.AppendLine(" *{box-sizing:border-box}");
|
||||
html.AppendLine(" .sig-header{position:fixed;top:0;left:0;right:0;height:46px;z-index:10;display:flex;align-items:center;justify-content:space-between;padding:0 18px;background:#2b3643;color:#fff;box-shadow:0 1px 4px rgba(0,0,0,.18)}");
|
||||
html.AppendLine(" .sig-brand{display:flex;align-items:center;gap:10px;font-size:15px;font-weight:600}");
|
||||
html.AppendLine(" .sig-brand-box{display:flex;align-items:center;justify-content:center;width:34px;height:30px;border-radius:3px;background:#3598dc;font-size:13px;font-weight:700;letter-spacing:.5px}");
|
||||
html.AppendLine(" .sig-header-status{display:flex;flex-direction:column;align-items:flex-end;color:#c6cfda;font-size:12px;line-height:16px}");
|
||||
html.AppendLine(" .sig-header-user{color:#fff;font-weight:600}");
|
||||
html.AppendLine(" .sig-sidebar{position:fixed;top:46px;bottom:0;left:0;width:235px;padding-top:18px;background:#364150}");
|
||||
html.AppendLine(" .sig-sidebar-title{padding:7px 20px 14px;color:#708096;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.6px}");
|
||||
html.AppendLine(" .sig-restored-menu{margin:0;padding:0;list-style:none}");
|
||||
html.AppendLine(" .sig-restored-menu li{margin:0;padding:0;list-style:none}");
|
||||
html.AppendLine(" .sig-restored-menu a,.sig-restored-menu button{display:flex;align-items:center;gap:12px;width:100%;padding:12px 20px;border:0;background:transparent;color:#b4bcc8;font-family:inherit;font-size:13px;text-decoration:none;text-align:left;cursor:pointer}");
|
||||
html.AppendLine(" .sig-restored-menu a:hover,.sig-restored-menu button:hover{background:#2c3542;color:#fff}");
|
||||
html.AppendLine(" .sig-restored-menu .sub-menu{display:none;margin:0;padding:0;background:#2c3542}");
|
||||
html.AppendLine(" .sig-restored-menu li.open>.sub-menu{display:block}");
|
||||
html.AppendLine(" .sig-restored-menu .sub-menu a{padding-left:42px;font-size:12px}");
|
||||
html.AppendLine(" .sig-main{min-height:100vh;margin-left:235px;padding:70px 28px 35px}");
|
||||
html.AppendLine(" .sig-page-title{margin:0 0 14px;color:#666;font-size:22px;font-weight:300}");
|
||||
html.AppendLine(" .sig-breadcrumb{margin-bottom:22px;padding:10px 14px;background:#fff;color:#888;font-size:12px}");
|
||||
html.AppendLine(" .sig-error-card{max-width:820px;background:#fff;border:1px solid #e5e5e5;border-left:4px solid #e43a45;box-shadow:0 2px 5px rgba(0,0,0,.07)}");
|
||||
html.AppendLine(" .sig-error-card-header{display:flex;align-items:center;gap:13px;padding:18px 22px;border-bottom:1px solid #eef1f5}");
|
||||
html.AppendLine(" .sig-error-icon{display:flex;align-items:center;justify-content:center;width:38px;height:38px;flex:0 0 38px;border-radius:50%;background:#fbeaec;color:#e43a45;font-size:21px;font-weight:700}");
|
||||
html.AppendLine(" .sig-error-title{margin:0 0 3px;color:#333;font-size:17px;font-weight:600}");
|
||||
html.AppendLine(" .sig-error-subtitle{color:#777;font-size:13px}");
|
||||
html.AppendLine(" .sig-error-body{padding:22px}");
|
||||
html.AppendLine(" .sig-error-body p{margin:0 0 14px;line-height:1.6}");
|
||||
html.AppendLine(" .sig-error-code-label{margin-bottom:6px;color:#777;font-size:12px;font-weight:600;text-transform:uppercase}");
|
||||
html.AppendLine(" .sig-error-code{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:11px 13px;background:#f4f6f8;border:1px solid #e7e9ec;border-radius:3px;font-family:Consolas,monospace;font-size:14px;font-weight:700}");
|
||||
html.AppendLine(" .sig-copy-button{border:0;background:transparent;color:#3598dc;font-family:inherit;font-size:12px;cursor:pointer}");
|
||||
html.AppendLine(" .sig-error-actions{display:flex;justify-content:flex-end;flex-wrap:wrap;gap:8px;margin-top:20px}");
|
||||
html.AppendLine(" .sig-btn{min-height:34px;padding:7px 15px;border:1px solid transparent;border-radius:2px;font-family:inherit;font-size:13px;cursor:pointer;text-decoration:none}");
|
||||
html.AppendLine(" .sig-btn-default{border-color:#d8d8d8;background:#fff;color:#555}");
|
||||
html.AppendLine(" .sig-btn-primary{border-color:#258fd7;background:#3598dc;color:#fff}");
|
||||
html.AppendLine(" .sig-btn-danger{border-color:#d52b37;background:#e43a45;color:#fff}");
|
||||
html.AppendLine(" .sig-btn:disabled{cursor:wait;opacity:.6}");
|
||||
html.AppendLine(" .sig-report-result{min-height:18px;margin-top:14px;color:#555;font-size:12px}");
|
||||
html.AppendLine(" .sig-error-details{margin-top:20px;border-top:1px solid #eee;padding-top:15px}");
|
||||
html.AppendLine(" .sig-error-details summary{color:#3598dc;font-size:13px;font-weight:600;cursor:pointer}");
|
||||
html.AppendLine(" .sig-error-details pre{max-height:380px;margin:13px 0 0;padding:14px;overflow:auto;white-space:pre-wrap;word-break:break-word;border-radius:3px;background:#263238;color:#f2f2f2;font-family:Consolas,monospace;font-size:12px;line-height:1.45}");
|
||||
html.AppendLine(" @media(max-width:800px){.sig-sidebar{display:none}.sig-main{margin-left:0;padding:65px 14px 25px}.sig-header-status{display:none}.sig-error-actions{flex-direction:column}.sig-btn{width:100%;text-align:center}}");
|
||||
html.AppendLine(" </style>");
|
||||
html.AppendLine("</head>");
|
||||
html.AppendLine("<body>");
|
||||
|
||||
html.AppendLine("<header class=\"sig-header\">");
|
||||
html.AppendLine(" <div class=\"sig-brand\">");
|
||||
html.AppendLine(" <div class=\"sig-brand-box\">SIG</div>");
|
||||
html.AppendLine(" <span>Sistema de Informacion Gerencial</span>");
|
||||
html.AppendLine(" </div>");
|
||||
html.AppendLine(" <div class=\"sig-header-status\">");
|
||||
if (!string.IsNullOrWhiteSpace(nombreUsuarioSeguro))
|
||||
{
|
||||
html.AppendLine(" <span class=\"sig-header-user\">" + nombreUsuarioSeguro + "</span>");
|
||||
}
|
||||
html.AppendLine(" </div>");
|
||||
html.AppendLine("</header>");
|
||||
|
||||
html.AppendLine("<aside class=\"sig-sidebar\">");
|
||||
html.AppendLine(" <div class=\"sig-sidebar-title\">Navegacion</div>");
|
||||
html.AppendLine(" <ul id=\"sigMenuRestaurado\" class=\"sig-restored-menu\">");
|
||||
html.AppendLine(" <li><a href=\"" + HttpUtility.HtmlAttributeEncode(urlInicio) + "\"><span>⌂</span><span>Inicio</span></a></li>");
|
||||
html.AppendLine(" <li><button type=\"button\" onclick=\"history.back();\"><span>←</span><span>Regresar</span></button></li>");
|
||||
html.AppendLine(" </ul>");
|
||||
html.AppendLine("</aside>");
|
||||
|
||||
html.AppendLine("<main class=\"sig-main\">");
|
||||
html.AppendLine(" <h1 class=\"sig-page-title\">Error inesperado</h1>");
|
||||
html.AppendLine(" <div class=\"sig-breadcrumb\">Inicio / Error</div>");
|
||||
html.AppendLine(" <section class=\"sig-error-card\">");
|
||||
html.AppendLine(" <div class=\"sig-error-card-header\">");
|
||||
html.AppendLine(" <div class=\"sig-error-icon\">!</div>");
|
||||
html.AppendLine(" <div>");
|
||||
html.AppendLine(" <h2 class=\"sig-error-title\">No fue posible completar la operacion</h2>");
|
||||
html.AppendLine(" <div class=\"sig-error-subtitle\">La aplicacion sigue disponible.</div>");
|
||||
html.AppendLine(" </div>");
|
||||
html.AppendLine(" </div>");
|
||||
html.AppendLine(" <div class=\"sig-error-body\">");
|
||||
html.AppendLine(" <p>Ocurrio un error inesperado al cargar esta seccion. Puedes regresar, intentar nuevamente o reportarlo al area de soporte.</p>");
|
||||
html.AppendLine(" <div class=\"sig-error-code-label\">Codigo para soporte</div>");
|
||||
html.AppendLine(" <div class=\"sig-error-code\">");
|
||||
html.AppendLine(" <span id=\"sigCodigoError\">" + codigoSeguro + "</span>");
|
||||
html.AppendLine(" <button type=\"button\" class=\"sig-copy-button\" onclick=\"sigCopiarCodigo();\">Copiar codigo</button>");
|
||||
html.AppendLine(" </div>");
|
||||
html.AppendLine(" <div id=\"sigResultadoReporte\" class=\"sig-report-result\"></div>");
|
||||
html.AppendLine(detalleHtml);
|
||||
html.AppendLine(" <div class=\"sig-error-actions\">");
|
||||
html.AppendLine(" <button type=\"button\" class=\"sig-btn sig-btn-default\" onclick=\"history.back();\">Regresar</button>");
|
||||
html.AppendLine(" <a class=\"sig-btn sig-btn-default\" href=\"" + HttpUtility.HtmlAttributeEncode(urlInicio) + "\">Ir al inicio</a>");
|
||||
html.AppendLine(" <button type=\"button\" class=\"sig-btn sig-btn-primary\" onclick=\"location.reload();\">Reintentar</button>");
|
||||
html.AppendLine(" <button type=\"button\" id=\"sigBtnReportar\" class=\"sig-btn sig-btn-danger\" onclick=\"sigReportarError();\">Reportar a soporte</button>");
|
||||
html.AppendLine(" </div>");
|
||||
html.AppendLine(" </div>");
|
||||
html.AppendLine(" </section>");
|
||||
html.AppendLine("</main>");
|
||||
|
||||
html.AppendLine("<script>");
|
||||
html.AppendLine("var sigCveUsuarioSesion='" + cveUsuarioJavaScript + "';");
|
||||
html.AppendLine("var sigCodigoError='" + codigoJavaScript + "';");
|
||||
html.AppendLine("var sigUrlReporte='" + HttpUtility.JavaScriptStringEncode(urlReporte) + "';");
|
||||
html.AppendLine("var sigMenuStorageKey='sig.currentMenu.v1';");
|
||||
|
||||
html.AppendLine("function sigBuscarSubMenu(elemento){");
|
||||
html.AppendLine(" var hijos=elemento.children;");
|
||||
html.AppendLine(" for(var i=0;i<hijos.length;i++){");
|
||||
html.AppendLine(" if(hijos[i].tagName==='UL' && (' '+hijos[i].className+' ').indexOf(' sub-menu ')>=0){return hijos[i];}");
|
||||
html.AppendLine(" }");
|
||||
html.AppendLine(" return null;");
|
||||
html.AppendLine("}");
|
||||
|
||||
html.AppendLine("function sigInicializarMenu(menu){");
|
||||
html.AppendLine(" menu.onclick=function(evento){");
|
||||
html.AppendLine(" evento=evento||window.event;");
|
||||
html.AppendLine(" var enlace=evento.target||evento.srcElement;");
|
||||
html.AppendLine(" while(enlace&&enlace!==menu&&enlace.tagName!=='A'){enlace=enlace.parentNode;}");
|
||||
html.AppendLine(" if(!enlace||enlace===menu){return;}");
|
||||
html.AppendLine(" var li=enlace.parentNode;");
|
||||
html.AppendLine(" var subMenu=sigBuscarSubMenu(li);");
|
||||
html.AppendLine(" if(!subMenu){return;}");
|
||||
html.AppendLine(" if(evento.preventDefault){evento.preventDefault();}else{evento.returnValue=false;}");
|
||||
html.AppendLine(" var abierto=(' '+li.className+' ').indexOf(' open ')>=0;");
|
||||
html.AppendLine(" li.className=abierto?li.className.replace(/\\bopen\\b/g,'').replace(/^\\s+|\\s+$/g,''):(li.className+' open').replace(/^\\s+|\\s+$/g,'');");
|
||||
html.AppendLine(" };");
|
||||
html.AppendLine("}");
|
||||
|
||||
html.AppendLine("function sigRestaurarMenu(){");
|
||||
html.AppendLine(" if(!sigCveUsuarioSesion){return;}");
|
||||
html.AppendLine(" try{");
|
||||
html.AppendLine(" var contenido=localStorage.getItem(sigMenuStorageKey);");
|
||||
html.AppendLine(" if(!contenido){return;}");
|
||||
html.AppendLine(" var datos=JSON.parse(contenido);");
|
||||
html.AppendLine(" if(!datos||!datos.menuHtml||!datos.cveUsuario){return;}");
|
||||
html.AppendLine(" if(String(datos.cveUsuario)!==String(sigCveUsuarioSesion)){return;}");
|
||||
html.AppendLine(" var menu=document.getElementById('sigMenuRestaurado');");
|
||||
html.AppendLine(" if(!menu){return;}");
|
||||
html.AppendLine(" menu.innerHTML=datos.menuHtml;");
|
||||
html.AppendLine(" sigInicializarMenu(menu);");
|
||||
html.AppendLine(" }catch(ex){if(window.console&&console.warn){console.warn('No fue posible restaurar el menu de SIG.',ex);}}");
|
||||
html.AppendLine("}");
|
||||
|
||||
html.AppendLine("function sigCopiarCodigo(){");
|
||||
html.AppendLine(" if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(sigCodigoError);}else{");
|
||||
html.AppendLine(" var temporal=document.createElement('textarea');");
|
||||
html.AppendLine(" temporal.value=sigCodigoError;");
|
||||
html.AppendLine(" document.body.appendChild(temporal);");
|
||||
html.AppendLine(" temporal.select();");
|
||||
html.AppendLine(" document.execCommand('copy');");
|
||||
html.AppendLine(" document.body.removeChild(temporal);");
|
||||
html.AppendLine(" }");
|
||||
html.AppendLine(" document.getElementById('sigResultadoReporte').textContent='El codigo fue copiado.';");
|
||||
html.AppendLine("}");
|
||||
|
||||
html.AppendLine("function sigReportarError(){");
|
||||
html.AppendLine(" var boton=document.getElementById('sigBtnReportar');");
|
||||
html.AppendLine(" var resultado=document.getElementById('sigResultadoReporte');");
|
||||
html.AppendLine(" boton.disabled=true;");
|
||||
html.AppendLine(" resultado.textContent='Enviando reporte...';");
|
||||
html.AppendLine(" var xhr=new XMLHttpRequest();");
|
||||
html.AppendLine(" xhr.open('POST',sigUrlReporte,true);");
|
||||
html.AppendLine(" xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded; charset=UTF-8');");
|
||||
html.AppendLine(" xhr.onreadystatechange=function(){");
|
||||
html.AppendLine(" if(xhr.readyState!==4){return;}");
|
||||
html.AppendLine(" if(xhr.status>=200&&xhr.status<300){resultado.textContent='El error fue reportado correctamente.';return;}");
|
||||
html.AppendLine(" boton.disabled=false;");
|
||||
html.AppendLine(" resultado.textContent='No fue posible enviar el reporte. Comunica el codigo a soporte.';");
|
||||
html.AppendLine(" };");
|
||||
html.AppendLine(" xhr.send('codigo='+encodeURIComponent(sigCodigoError)+'&ruta='+encodeURIComponent(window.location.href));");
|
||||
html.AppendLine("}");
|
||||
|
||||
html.AppendLine("sigRestaurarMenu();");
|
||||
html.AppendLine("</script>");
|
||||
html.AppendLine("</body>");
|
||||
html.AppendLine("</html>");
|
||||
|
||||
return html.ToString();
|
||||
}
|
||||
|
||||
public static string ObtenerDetalleTecnico(
|
||||
Exception exception)
|
||||
{
|
||||
if (exception == null)
|
||||
{
|
||||
return "No se encontro informacion de la excepcion.";
|
||||
}
|
||||
|
||||
StringBuilder texto = new StringBuilder();
|
||||
Exception actual = exception;
|
||||
int nivel = 1;
|
||||
|
||||
while (actual != null)
|
||||
{
|
||||
texto.AppendLine(
|
||||
"EXCEPCION " + nivel);
|
||||
|
||||
texto.AppendLine(
|
||||
"Tipo: " + actual.GetType().FullName);
|
||||
|
||||
texto.AppendLine(
|
||||
"Mensaje: " + actual.Message);
|
||||
|
||||
HttpParseException parseException =
|
||||
actual as HttpParseException;
|
||||
|
||||
if (parseException != null)
|
||||
{
|
||||
texto.AppendLine(
|
||||
"Archivo: " + parseException.VirtualPath);
|
||||
|
||||
texto.AppendLine(
|
||||
"Linea: " + parseException.Line);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(
|
||||
actual.StackTrace))
|
||||
{
|
||||
texto.AppendLine();
|
||||
texto.AppendLine("Stack trace:");
|
||||
texto.AppendLine(actual.StackTrace);
|
||||
}
|
||||
|
||||
texto.AppendLine();
|
||||
texto.AppendLine(
|
||||
"--------------------------------------------------");
|
||||
texto.AppendLine();
|
||||
|
||||
actual = actual.InnerException;
|
||||
nivel++;
|
||||
}
|
||||
|
||||
return texto.ToString();
|
||||
}
|
||||
|
||||
private static string ObtenerCodigoExistente(
|
||||
Exception exception)
|
||||
{
|
||||
Exception actual = exception;
|
||||
|
||||
while (actual != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
object valor =
|
||||
actual.Data[ErrorCodeDataKey];
|
||||
|
||||
if (valor != null)
|
||||
{
|
||||
return valor.ToString();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
actual = actual.InnerException;
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private static void AsignarCodigo(
|
||||
Exception exception,
|
||||
string codigo)
|
||||
{
|
||||
Exception actual = exception;
|
||||
|
||||
while (actual != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
actual.Data[ErrorCodeDataKey] = codigo;
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
actual = actual.InnerException;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
106
SIG.Web/AppCode/Services/Logging/ExceptionLogger.cs
Normal file
106
SIG.Web/AppCode/Services/Logging/ExceptionLogger.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
|
||||
namespace SIG.Web.AppCode.Services.Logging
|
||||
{
|
||||
public static class ExceptionLogger
|
||||
{
|
||||
private static readonly object LockObject = new object();
|
||||
|
||||
public static void Log(Exception exception, string source)
|
||||
{
|
||||
Log(exception, source, null);
|
||||
}
|
||||
|
||||
public static void Log(Exception exception, string source, string userMessage)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (exception == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string logPath = GetLogPath();
|
||||
string directory = Path.GetDirectoryName(logPath);
|
||||
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
string content = BuildLogContent(exception, source, userMessage);
|
||||
|
||||
lock (LockObject)
|
||||
{
|
||||
File.AppendAllText(logPath, content, Encoding.UTF8);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Never throw from the logger.
|
||||
// If the logger fails, the application must continue.
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetLogPath()
|
||||
{
|
||||
string configuredPath = ConfigurationManager.AppSettings["ExceptionLogFilePath"];
|
||||
|
||||
if (string.IsNullOrWhiteSpace(configuredPath))
|
||||
{
|
||||
configuredPath = "~/log/log.txt";
|
||||
}
|
||||
|
||||
if (HttpContext.Current != null)
|
||||
{
|
||||
return HttpContext.Current.Server.MapPath(configuredPath);
|
||||
}
|
||||
|
||||
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log", "log.txt");
|
||||
}
|
||||
|
||||
private static string BuildLogContent(Exception exception, string source, string userMessage)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine("============================================================");
|
||||
sb.AppendLine("Date: " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));
|
||||
sb.AppendLine("Source: " + Safe(source));
|
||||
sb.AppendLine("UserMessage: " + Safe(userMessage));
|
||||
|
||||
HttpContext context = HttpContext.Current;
|
||||
|
||||
if (context != null && context.Request != null)
|
||||
{
|
||||
sb.AppendLine("Url: " + Safe(context.Request.Url != null ? context.Request.Url.ToString() : null));
|
||||
sb.AppendLine("HttpMethod: " + Safe(context.Request.HttpMethod));
|
||||
sb.AppendLine("DireccionIP: " + Safe(context.Request.UserHostAddress));
|
||||
sb.AppendLine("AgenteUsuario: " + Safe(context.Request.UserAgent));
|
||||
}
|
||||
|
||||
if (context != null && context.User != null && context.User.Identity != null)
|
||||
{
|
||||
sb.AppendLine("Identity: " + Safe(context.User.Identity.Name));
|
||||
sb.AppendLine("IsAuthenticated: " + context.User.Identity.IsAuthenticated);
|
||||
}
|
||||
|
||||
sb.AppendLine("ExceptionType: " + exception.GetType().FullName);
|
||||
sb.AppendLine("Message: " + exception.Message);
|
||||
sb.AppendLine("BaseException: " + exception.GetBaseException().Message);
|
||||
sb.AppendLine("StackTrace:");
|
||||
sb.AppendLine(exception.ToString());
|
||||
sb.AppendLine();
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string Safe(string value)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(value) ? string.Empty : value;
|
||||
}
|
||||
}
|
||||
}
|
||||
31
SIG.Web/AppCode/Services/Login/DummyLoginService.cs
Normal file
31
SIG.Web/AppCode/Services/Login/DummyLoginService.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using SIG.Web.AppCode.Contracts.Login;
|
||||
using SIG.Web.AppCode.Session;
|
||||
|
||||
namespace SIG.Web.AppCode.Services.Login
|
||||
{
|
||||
public sealed class DummyLoginService : ILoginService
|
||||
{
|
||||
public LoginResult Login(string nombreUsuario, string contrasena, string nombreDominio)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(nombreUsuario))
|
||||
{
|
||||
return LoginResult.Fail("Escriba un nombre de usuario.");
|
||||
}
|
||||
|
||||
return LoginResult.Ok(new UserSessionModel
|
||||
{
|
||||
CveUsuario = 15,
|
||||
CveRol = 1,
|
||||
CveEmpresa = 1,
|
||||
NombreUsuario = nombreUsuario.Trim(),
|
||||
Nombre = "Usuario Demo",
|
||||
CorreoElectronico = "usuario.demo@example.invalid",
|
||||
NombreRol = "Administrador",
|
||||
NombreDominio = nombreDominio,
|
||||
TokenSesion = Guid.NewGuid().ToString("N"),
|
||||
DebeCambiarContrasena = false
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
21
SIG.Web/AppCode/Services/Menu/DummyMenuProvider.cs
Normal file
21
SIG.Web/AppCode/Services/Menu/DummyMenuProvider.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System.Collections.Generic;
|
||||
using SIG.Web.AppCode.Session;
|
||||
|
||||
namespace SIG.Web.AppCode.Services.Menu
|
||||
{
|
||||
public sealed class DummyMenuProvider : IMenuProvider
|
||||
{
|
||||
public IReadOnlyList<MenuItemModel> GetMenu(UserSessionModel user)
|
||||
{
|
||||
return new List<MenuItemModel>
|
||||
{
|
||||
new MenuItemModel { NombreModulo = "Home", Url = "~/Inicio.aspx", ClaseCssIcono = "icon-home" },
|
||||
new MenuItemModel { NombreModulo = "Developer tools", Url = "#", ClaseCssIcono = "icon-puzzle", Hijos =
|
||||
{
|
||||
new MenuItemModel { NombreModulo = "Blank page", Url = "~/Inicio.aspx", ClaseCssIcono = "icon-doc" }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
85
SIG.Web/AppCode/Services/Menu/MenuProvider.cs
Normal file
85
SIG.Web/AppCode/Services/Menu/MenuProvider.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using SIG.Web.AppCode.Contracts.Database;
|
||||
using SIG.Web.AppCode.Services.Database;
|
||||
using SIG.Web.AppCode.Utilities;
|
||||
using SIG.Web.AppCode.Session;
|
||||
|
||||
namespace SIG.Web.AppCode.Services.Menu
|
||||
{
|
||||
public sealed class MenuProvider : IMenuProvider
|
||||
{
|
||||
private readonly IDbExecutor _dbExecutor;
|
||||
|
||||
public MenuProvider(IDbExecutor dbExecutor)
|
||||
{
|
||||
_dbExecutor = dbExecutor;
|
||||
}
|
||||
|
||||
public IReadOnlyList<MenuItemModel> GetMenu(UserSessionModel user)
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
return new List<MenuItemModel>();
|
||||
}
|
||||
|
||||
var modulesResult = GetModules(user);
|
||||
|
||||
if (!modulesResult.Success || modulesResult.Data == null)
|
||||
{
|
||||
return new List<MenuItemModel>();
|
||||
}
|
||||
|
||||
return BuildTree(modulesResult.Data);
|
||||
}
|
||||
|
||||
private SIG.Web.AppCode.Models.Common.DbResult<List<MenuItemModel>> GetModules(UserSessionModel user)
|
||||
{
|
||||
return _dbExecutor.Execute(user.NombreDominio, connection =>
|
||||
{
|
||||
using (IDbCommand command = connection.CreateCommand())
|
||||
{
|
||||
StoredProcedureCommand.Configure(command, "dbo.PA_Sis_MenuList", user.CveUsuario);
|
||||
StoredProcedureCommand.AddInput(command, "@CveRol", user.CveRol, DbType.Int32);
|
||||
|
||||
List<MenuItemModel> items;
|
||||
|
||||
using (IDataReader reader = command.ExecuteReader())
|
||||
{
|
||||
items = reader.ToList<MenuItemModel>();
|
||||
}
|
||||
|
||||
StoredProcedureCommand.EnsureSuccess(command);
|
||||
return items;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static IReadOnlyList<MenuItemModel> BuildTree(List<MenuItemModel> flatItems)
|
||||
{
|
||||
if (flatItems == null || flatItems.Count == 0)
|
||||
{
|
||||
return new List<MenuItemModel>();
|
||||
}
|
||||
|
||||
var byId = flatItems.ToDictionary(x => x.CveModulo);
|
||||
var roots = new List<MenuItemModel>();
|
||||
|
||||
foreach (var item in flatItems.OrderBy(x => x.OrdenVisualizacion).ThenBy(x => x.NombreModulo))
|
||||
{
|
||||
if (item.CveModuloPadre.HasValue && byId.ContainsKey(item.CveModuloPadre.Value))
|
||||
{
|
||||
byId[item.CveModuloPadre.Value].Hijos.Add(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
roots.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
return roots;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.Collections.Generic;
|
||||
using SIG.Web.AppCode.Contracts.Search;
|
||||
using SIG.Web.AppCode.Session;
|
||||
|
||||
namespace SIG.Web.AppCode.Services.Search
|
||||
{
|
||||
public sealed class DummyBuscadorCatalogoDataProvider : IBuscadorCatalogoDataProvider
|
||||
{
|
||||
public BuscadorCatalogoCallbackRespuesta Search(BuscadorCatalogoCallbackSolicitud request, UserSessionModel user)
|
||||
{
|
||||
var response = new BuscadorCatalogoCallbackRespuesta { Total = 1 };
|
||||
response.Rows.Add(new Dictionary<string, object>
|
||||
{
|
||||
["Id"] = 1,
|
||||
["Descripcion"] = "Dummy result"
|
||||
});
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SIG.Web/AppCode/Services/Security/LoginService.cs
Normal file
118
SIG.Web/AppCode/Services/Security/LoginService.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using SIG.Web.AppCode.Contracts.Security;
|
||||
using SIG.Web.AppCode.Models.Security;
|
||||
using SIG.Web.AppCode.Session;
|
||||
|
||||
namespace SIG.Web.AppCode.Services.Security
|
||||
{
|
||||
public sealed class LoginService : ILoginService
|
||||
{
|
||||
private const string GenericError = "No fue posible iniciar sesion. Verifique sus datos.";
|
||||
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IPasswordHasher _passwordHasher;
|
||||
private readonly ILoginAttemptRepository _loginAttemptRepository;
|
||||
private readonly IUserSessionRepository _userSessionRepository;
|
||||
|
||||
public LoginService(
|
||||
IUserRepository userRepository,
|
||||
IPasswordHasher passwordHasher,
|
||||
ILoginAttemptRepository loginAttemptRepository,
|
||||
IUserSessionRepository userSessionRepository)
|
||||
{
|
||||
_userRepository = userRepository;
|
||||
_passwordHasher = passwordHasher;
|
||||
_loginAttemptRepository = loginAttemptRepository;
|
||||
_userSessionRepository = userSessionRepository;
|
||||
}
|
||||
|
||||
public LoginResult Login(LoginRequest request)
|
||||
{
|
||||
if (request == null || string.IsNullOrWhiteSpace(request.NombreUsuario) || string.IsNullOrWhiteSpace(request.Contrasena))
|
||||
{
|
||||
return LoginResult.Fail(GenericError);
|
||||
}
|
||||
|
||||
string username = request.NombreUsuario.Trim();
|
||||
var userResult = _userRepository.ObtenerPorNombreUsuario(username, request.NombreDominio);
|
||||
|
||||
if (!userResult.Success)
|
||||
{
|
||||
return LoginResult.Fail(userResult.UserMessage);
|
||||
}
|
||||
|
||||
UserModel user = userResult.Data;
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
_loginAttemptRepository.Save(null, username, false, "UserNotFound", request.DireccionIP, request.AgenteUsuario, request.NombreDominio);
|
||||
return LoginResult.Fail(GenericError);
|
||||
}
|
||||
|
||||
if (!user.CorreoVerificado)
|
||||
{
|
||||
_loginAttemptRepository.Save(user.CveUsuario, username, false, "EmailNotVerified", request.DireccionIP, request.AgenteUsuario, request.NombreDominio);
|
||||
return LoginResult.Fail("Debes verificar tu correo electrónico antes de iniciar sesión.");
|
||||
}
|
||||
|
||||
if (!user.Activo)
|
||||
{
|
||||
_loginAttemptRepository.Save(user.CveUsuario, username, false, "InactiveUser", request.DireccionIP, request.AgenteUsuario, request.NombreDominio);
|
||||
return LoginResult.Fail("El usuario se encuentra inactivo.");
|
||||
}
|
||||
|
||||
if (user.BloqueadoHasta.HasValue && user.BloqueadoHasta.Value > DateTime.Now) {
|
||||
_loginAttemptRepository.Save(user.CveUsuario, username, false, "LockedUser", request.DireccionIP, request.AgenteUsuario, request.NombreDominio);
|
||||
return LoginResult.Fail("El usuario se encuentra bloqueado temporalmente.");
|
||||
}
|
||||
|
||||
if (!_passwordHasher.VerifyPassword(request.Contrasena, user.HashContrasena))
|
||||
{
|
||||
_userRepository.RegisterFailedLogin(user.CveUsuario, request.NombreDominio);
|
||||
_loginAttemptRepository.Save(user.CveUsuario, username, false, "InvalidPassword", request.DireccionIP, request.AgenteUsuario, request.NombreDominio);
|
||||
return LoginResult.Fail(GenericError);
|
||||
}
|
||||
|
||||
string sessionToken = Guid.NewGuid().ToString("N");
|
||||
|
||||
var loginUpdateResult = _userRepository.RegisterSuccessfulLogin(user.CveUsuario, request.NombreDominio);
|
||||
|
||||
if (!loginUpdateResult.Success)
|
||||
{
|
||||
return LoginResult.Fail(loginUpdateResult.UserMessage);
|
||||
}
|
||||
|
||||
var sessionResult = _userSessionRepository.StartSession(user.CveUsuario, sessionToken, request.DireccionIP, request.AgenteUsuario, request.NombreDominio);
|
||||
|
||||
if (!sessionResult.Success)
|
||||
{
|
||||
return LoginResult.Fail(sessionResult.UserMessage);
|
||||
}
|
||||
|
||||
_loginAttemptRepository.Save(user.CveUsuario, username, true, null, request.DireccionIP, request.AgenteUsuario, request.NombreDominio);
|
||||
|
||||
var sessionUser = new UserSessionModel
|
||||
{
|
||||
CveUsuario = user.CveUsuario,
|
||||
CveRol = user.CveRol,
|
||||
NombreUsuario = user.NombreUsuario,
|
||||
Nombre = user.NombreUsuario,
|
||||
CorreoElectronico = user.CorreoElectronico,
|
||||
NombreRol = user.NombreRol,
|
||||
NombreDominio = request.NombreDominio,
|
||||
TokenSesion = sessionToken,
|
||||
DebeCambiarContrasena = user.DebeCambiarContrasena
|
||||
};
|
||||
|
||||
return LoginResult.Ok(sessionUser);
|
||||
}
|
||||
|
||||
public void Logout(string sessionToken, string domainName)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(sessionToken))
|
||||
{
|
||||
_userSessionRepository.EndSession(sessionToken, domainName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
SIG.Web/AppCode/Services/Security/PasswordHasher.cs
Normal file
40
SIG.Web/AppCode/Services/Security/PasswordHasher.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using SIG.Web.AppCode.Contracts.Security;
|
||||
namespace SIG.Web.AppCode.Services.Security
|
||||
{
|
||||
public sealed class PasswordHasher : IPasswordHasher
|
||||
{
|
||||
private const int SaltSize = 16;
|
||||
private const int HashSize = 32;
|
||||
private const int Iterations = 100000;
|
||||
public string HashPassword(string password)
|
||||
{
|
||||
byte[] salt = new byte[SaltSize];
|
||||
using (var rng = RandomNumberGenerator.Create()) rng.GetBytes(salt);
|
||||
byte[] hash = Derive(password, salt, Iterations);
|
||||
return "PBKDF2-SHA256$" + Iterations + "$" + Convert.ToBase64String(salt) + "$" + Convert.ToBase64String(hash);
|
||||
}
|
||||
public bool VerifyPassword(string password, string storedHash)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(password) || string.IsNullOrWhiteSpace(storedHash)) return false;
|
||||
string[] parts = storedHash.Split('$');
|
||||
if (parts.Length != 4 || parts[0] != "PBKDF2-SHA256") return false;
|
||||
int iterations; if (!int.TryParse(parts[1], out iterations)) return false;
|
||||
byte[] salt = Convert.FromBase64String(parts[2]);
|
||||
byte[] expected = Convert.FromBase64String(parts[3]);
|
||||
byte[] actual = Derive(password, salt, iterations);
|
||||
return FixedTimeEquals(actual, expected);
|
||||
}
|
||||
private static byte[] Derive(string password, byte[] salt, int iterations)
|
||||
{
|
||||
using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations, HashAlgorithmName.SHA256)) return pbkdf2.GetBytes(HashSize);
|
||||
}
|
||||
private static bool FixedTimeEquals(byte[] left, byte[] right)
|
||||
{
|
||||
if (left == null || right == null || left.Length != right.Length) return false;
|
||||
int diff = 0; for (int i = 0; i < left.Length; i++) diff |= left[i] ^ right[i];
|
||||
return diff == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
101
SIG.Web/AppCode/Services/Security/PermissionService.cs
Normal file
101
SIG.Web/AppCode/Services/Security/PermissionService.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using SIG.Web.AppCode.Contracts.Database;
|
||||
using SIG.Web.AppCode.Contracts.Security;
|
||||
using SIG.Web.AppCode.Services.Database;
|
||||
|
||||
namespace SIG.Web.AppCode.Services.Security
|
||||
{
|
||||
public sealed class PermissionService : IPermissionService
|
||||
{
|
||||
private readonly IDbExecutor _dbExecutor;
|
||||
|
||||
public PermissionService(IDbExecutor dbExecutor)
|
||||
{
|
||||
_dbExecutor = dbExecutor;
|
||||
}
|
||||
|
||||
public bool HasPermission(int cveUser, string permissionCode)
|
||||
{
|
||||
if (cveUser <= 0 || string.IsNullOrWhiteSpace(permissionCode))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var result = _dbExecutor.Execute(null, connection =>
|
||||
{
|
||||
using (IDbCommand command = connection.CreateCommand())
|
||||
{
|
||||
StoredProcedureCommand.Configure(command, "dbo.PA_Sis_PermisoCheck", cveUser);
|
||||
StoredProcedureCommand.AddInput(command, "@CodigoPermiso", permissionCode.Trim(), DbType.String);
|
||||
|
||||
object value = command.ExecuteScalar();
|
||||
StoredProcedureCommand.EnsureSuccess(command);
|
||||
return value != null && value != DBNull.Value && Convert.ToBoolean(value);
|
||||
}
|
||||
});
|
||||
|
||||
return result.Success && result.Data;
|
||||
}
|
||||
|
||||
public bool CanAccessPage(int cveUser, string pageUrl)
|
||||
{
|
||||
if (cveUser <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string normalizedUrl = NormalizePageUrl(pageUrl);
|
||||
|
||||
if (IsPublicPage(normalizedUrl))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var result = _dbExecutor.Execute(null, connection =>
|
||||
{
|
||||
using (IDbCommand command = connection.CreateCommand())
|
||||
{
|
||||
StoredProcedureCommand.Configure(command, "dbo.PA_Sis_PaginaAccessCheck", cveUser);
|
||||
StoredProcedureCommand.AddInput(command, "@PaginaUrl", normalizedUrl, DbType.String);
|
||||
|
||||
object value = command.ExecuteScalar();
|
||||
StoredProcedureCommand.EnsureSuccess(command);
|
||||
return value != null && value != DBNull.Value && Convert.ToBoolean(value);
|
||||
}
|
||||
});
|
||||
|
||||
return result.Success && result.Data;
|
||||
}
|
||||
|
||||
private static string NormalizePageUrl(string pageUrl)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(pageUrl))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
string value = pageUrl.Trim();
|
||||
|
||||
if (value.StartsWith("~/", StringComparison.Ordinal))
|
||||
{
|
||||
value = value.Substring(2);
|
||||
}
|
||||
|
||||
if (value.StartsWith("/", StringComparison.Ordinal))
|
||||
{
|
||||
value = value.Substring(1);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static bool IsPublicPage(string normalizedUrl)
|
||||
{
|
||||
return normalizedUrl.Equals("Login.aspx", StringComparison.OrdinalIgnoreCase) ||
|
||||
normalizedUrl.Equals("Logout.aspx", StringComparison.OrdinalIgnoreCase) ||
|
||||
normalizedUrl.Equals("AccessDenied.aspx", StringComparison.OrdinalIgnoreCase) ||
|
||||
normalizedUrl.Equals("Error.aspx", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using SIG.Web.AppCode.Contracts.Security;
|
||||
using SIG.Web.AppCode.Models.Common;
|
||||
using SIG.Web.AppCode.Models.Security;
|
||||
|
||||
namespace SIG.Web.AppCode.Services.Security
|
||||
{
|
||||
public sealed class PublicRegistrationService : IPublicRegistrationService
|
||||
{
|
||||
private readonly IUserAdministrationRepository _repository;
|
||||
private readonly IPasswordHasher _passwordHasher;
|
||||
private readonly IEmailSender _emailSender;
|
||||
public PublicRegistrationService(IUserAdministrationRepository repository, IPasswordHasher passwordHasher, IEmailSender emailSender) { _repository = repository; _passwordHasher = passwordHasher; _emailSender = emailSender; }
|
||||
|
||||
public DbResult<bool> Validate(PublicRegistrationDraft draft, string domainName)
|
||||
{
|
||||
string error = ValidateRequired(draft);
|
||||
return error == null ? _repository.ValidateRegistration(draft.NombreUsuario, draft.CorreoElectronico, draft.RFC, domainName) : DbResult<bool>.Fail(error, error, null);
|
||||
}
|
||||
|
||||
public DbResult<List<StateOptionModel>> ListStates(string domainName) { return _repository.ListStates(domainName); }
|
||||
|
||||
public DbResult<bool> Register(PublicRegistrationDraft draft, string verificationBaseUrl, string domainName)
|
||||
{
|
||||
DbResult<bool> validation = Validate(draft, domainName); if (!validation.Success) return validation;
|
||||
if (!_emailSender.IsConfigured) return DbResult<bool>.Fail("El envío de correo todavía no está configurado. Contacta a Sistemas.", "SMTP no configurado.", null);
|
||||
string token = CreateToken();
|
||||
DbResult<RegistrationCreatedModel> created = _repository.CreateRegistration(draft, _passwordHasher.HashPassword(draft.Contrasena), HashToken(token), domainName);
|
||||
if (!created.Success) return DbResult<bool>.Fail(created.UserMessage, created.TechnicalMessage, created.Exception);
|
||||
try
|
||||
{
|
||||
_emailSender.SendVerification(draft.CorreoElectronico, verificationBaseUrl + (verificationBaseUrl.Contains("?") ? "&" : "?") + "token=" + Uri.EscapeDataString(token));
|
||||
return DbResult<bool>.Ok(true);
|
||||
}
|
||||
catch (Exception ex) { return DbResult<bool>.Fail("La cuenta quedó pendiente, pero no fue posible enviar el correo. Contacta a Sistemas para reenviarlo.", ex.Message, ex); }
|
||||
}
|
||||
|
||||
public DbResult<bool> Verify(string token, string domainName)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(token) ? DbResult<bool>.Fail("La liga de verificación no es válida.", "Token vacío.", null) : _repository.VerifyEmail(HashToken(token), domainName);
|
||||
}
|
||||
|
||||
private static string ValidateRequired(PublicRegistrationDraft d)
|
||||
{
|
||||
if (d == null) return "Captura los datos del registro.";
|
||||
if (string.IsNullOrWhiteSpace(d.NombreUsuario) || string.IsNullOrWhiteSpace(d.CorreoElectronico) || string.IsNullOrWhiteSpace(d.Contrasena)) return "Completa los datos de usuario.";
|
||||
if (d.Contrasena.Length < 8) return "La contraseña debe tener al menos 8 caracteres.";
|
||||
if (string.IsNullOrWhiteSpace(d.RFC) || string.IsNullOrWhiteSpace(d.RazonSocial) || string.IsNullOrWhiteSpace(d.Direccion) || string.IsNullOrWhiteSpace(d.Colonia) || string.IsNullOrWhiteSpace(d.Ciudad) || d.CveSATEstado <= 0) return "Completa los datos obligatorios de la empresa.";
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string CreateToken() { byte[] bytes = new byte[32]; using (var rng = RandomNumberGenerator.Create()) rng.GetBytes(bytes); return Convert.ToBase64String(bytes).TrimEnd('=').Replace('+', '-').Replace('/', '_'); }
|
||||
private static string HashToken(string token) { using (SHA256 sha = SHA256.Create()) { byte[] hash = sha.ComputeHash(Encoding.UTF8.GetBytes(token)); var value = new StringBuilder(64); foreach (byte item in hash) value.Append(item.ToString("x2")); return value.ToString(); } }
|
||||
}
|
||||
}
|
||||
31
SIG.Web/AppCode/Services/Security/SmtpEmailSender.cs
Normal file
31
SIG.Web/AppCode/Services/Security/SmtpEmailSender.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.Net;
|
||||
using System.Net.Mail;
|
||||
using SIG.Web.AppCode.Contracts.Security;
|
||||
|
||||
namespace SIG.Web.AppCode.Services.Security
|
||||
{
|
||||
public sealed class SmtpEmailSender : IEmailSender
|
||||
{
|
||||
public bool IsConfigured { get { return !string.IsNullOrWhiteSpace(ConfigurationManager.AppSettings["SmtpHost"]) && !string.IsNullOrWhiteSpace(ConfigurationManager.AppSettings["EmailFrom"]); } }
|
||||
|
||||
public void SendVerification(string recipient, string verificationUrl)
|
||||
{
|
||||
if (!IsConfigured) throw new InvalidOperationException("El envío de correo no se encuentra configurado.");
|
||||
int port; if (!int.TryParse(ConfigurationManager.AppSettings["SmtpPort"], out port)) port = 25;
|
||||
bool ssl; bool.TryParse(ConfigurationManager.AppSettings["SmtpEnableSsl"], out ssl);
|
||||
using (var message = new MailMessage(ConfigurationManager.AppSettings["EmailFrom"], recipient))
|
||||
using (var client = new SmtpClient(ConfigurationManager.AppSettings["SmtpHost"], port))
|
||||
{
|
||||
message.Subject = "Verifica tu correo - Plataforma de Desarrolladores";
|
||||
message.IsBodyHtml = true;
|
||||
message.Body = "<p>Para concluir la creación de tu cuenta, abre la siguiente liga:</p><p><a href=\"" + WebUtility.HtmlEncode(verificationUrl) + "\">Verificar mi correo</a></p><p>La liga vence en 24 horas.</p>";
|
||||
client.EnableSsl = ssl;
|
||||
string user = ConfigurationManager.AppSettings["SmtpUser"];
|
||||
if (!string.IsNullOrWhiteSpace(user)) client.Credentials = new NetworkCredential(user, ConfigurationManager.AppSettings["SmtpPassword"]);
|
||||
client.Send(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Web;
|
||||
using SIG.Web.AppCode.Contracts.Security;
|
||||
using SIG.Web.AppCode.Session;
|
||||
namespace SIG.Web.AppCode.Services.Security
|
||||
{
|
||||
public sealed class WebFormsUserContextProvider : Contracts.Security.IUserContextProvider
|
||||
{
|
||||
public bool IsAuthenticated() { return GetCurrentUser() != null; }
|
||||
public UserSessionModel GetCurrentUser()
|
||||
{
|
||||
return HttpContext.Current == null || HttpContext.Current.Session == null ? null : HttpContext.Current.Session[SessionKeys.CurrentUser] as UserSessionModel;
|
||||
}
|
||||
public void SetCurrentUser(UserSessionModel user)
|
||||
{
|
||||
if (HttpContext.Current == null || HttpContext.Current.Session == null)
|
||||
return; HttpContext.Current.Session[SessionKeys.CurrentUser] = user;
|
||||
HttpContext.Current.Session[SessionKeys.TokenSesion] = user != null ? user.TokenSesion : null;
|
||||
}
|
||||
public void Clear()
|
||||
{
|
||||
if (HttpContext.Current == null || HttpContext.Current.Session == null)
|
||||
return; HttpContext.Current.Session.Remove(SessionKeys.CurrentUser);
|
||||
HttpContext.Current.Session.Remove(SessionKeys.TokenSesion);
|
||||
}
|
||||
}
|
||||
}
|
||||
9
SIG.Web/AppCode/Session/IMenuProvider.cs
Normal file
9
SIG.Web/AppCode/Session/IMenuProvider.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SIG.Web.AppCode.Session
|
||||
{
|
||||
public interface IMenuProvider
|
||||
{
|
||||
IReadOnlyList<MenuItemModel> GetMenu(UserSessionModel user);
|
||||
}
|
||||
}
|
||||
10
SIG.Web/AppCode/Session/ISessionContext.cs
Normal file
10
SIG.Web/AppCode/Session/ISessionContext.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace SIG.Web.AppCode.Session
|
||||
{
|
||||
public interface ISessionContext
|
||||
{
|
||||
T Get<T>(string key) where T : class;
|
||||
void Set<T>(string key, T value) where T : class;
|
||||
void Remove(string key);
|
||||
void Clear();
|
||||
}
|
||||
}
|
||||
19
SIG.Web/AppCode/Session/MenuItemModel.cs
Normal file
19
SIG.Web/AppCode/Session/MenuItemModel.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SIG.Web.AppCode.Session
|
||||
{
|
||||
[Serializable]
|
||||
public sealed class MenuItemModel
|
||||
{
|
||||
public int CveModulo { get; set; }
|
||||
public int? CveModuloPadre { get; set; }
|
||||
public string NombreModulo { get; set; }
|
||||
public string Url { get; set; }
|
||||
public string ClaseCssIcono { get; set; }
|
||||
public string ClaseCssEstilo { get; set; }
|
||||
public int OrdenVisualizacion { get; set; }
|
||||
public bool Visible { get; set; } = true;
|
||||
public List<MenuItemModel> Hijos { get; set; } = new List<MenuItemModel>();
|
||||
}
|
||||
}
|
||||
8
SIG.Web/AppCode/Session/SessionKeys.cs
Normal file
8
SIG.Web/AppCode/Session/SessionKeys.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace SIG.Web.AppCode.Session
|
||||
{
|
||||
public static class SessionKeys
|
||||
{
|
||||
public const string CurrentUser = "SIG_CurrentUser";
|
||||
public const string TokenSesion = "SIG_SessionToken";
|
||||
}
|
||||
}
|
||||
16
SIG.Web/AppCode/Session/UserSessionModel.cs
Normal file
16
SIG.Web/AppCode/Session/UserSessionModel.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace SIG.Web.AppCode.Session
|
||||
{
|
||||
public sealed class UserSessionModel
|
||||
{
|
||||
public int CveUsuario { get; set; }
|
||||
public int CveRol { get; set; }
|
||||
public int CveEmpresa { get; set; }
|
||||
public string NombreUsuario { get; set; }
|
||||
public string Nombre { get; set; }
|
||||
public string CorreoElectronico { get; set; }
|
||||
public string NombreRol { get; set; }
|
||||
public string NombreDominio { get; set; }
|
||||
public string TokenSesion { get; set; }
|
||||
public bool DebeCambiarContrasena { get; set; }
|
||||
}
|
||||
}
|
||||
28
SIG.Web/AppCode/Session/WebFormsSessionContext.cs
Normal file
28
SIG.Web/AppCode/Session/WebFormsSessionContext.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System.Web;
|
||||
|
||||
namespace SIG.Web.AppCode.Session
|
||||
{
|
||||
public sealed class WebFormsSessionContext : ISessionContext
|
||||
{
|
||||
public T Get<T>(string key) where T : class
|
||||
{
|
||||
return HttpContext.Current?.Session?[key] as T;
|
||||
}
|
||||
|
||||
public void Set<T>(string key, T value) where T : class
|
||||
{
|
||||
if (HttpContext.Current?.Session == null) return;
|
||||
HttpContext.Current.Session[key] = value;
|
||||
}
|
||||
|
||||
public void Remove(string key)
|
||||
{
|
||||
HttpContext.Current?.Session?.Remove(key);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
HttpContext.Current?.Session?.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
74
SIG.Web/AppCode/SigControls/BuscadorCatalogo.cs
Normal file
74
SIG.Web/AppCode/SigControls/BuscadorCatalogo.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
using Newtonsoft.Json;
|
||||
using SIG.Web.AppCode.Configuration;
|
||||
using SIG.Web.AppCode.Contracts.Search;
|
||||
|
||||
namespace SIG.Web.AppCode.SigControls
|
||||
{
|
||||
[ToolboxData("<{0}:BuscadorCatalogo runat=server></{0}:BuscadorCatalogo>")]
|
||||
public class BuscadorCatalogo : CompositeControl, INamingContainer
|
||||
{
|
||||
private TextBox _textBox;
|
||||
private HiddenField _value;
|
||||
private Button _button;
|
||||
|
||||
[Category("SIG")]
|
||||
public string Catalogo
|
||||
{
|
||||
get { return ViewState["Catalogo"] as string ?? string.Empty; }
|
||||
set { ViewState["Catalogo"] = value; }
|
||||
}
|
||||
|
||||
[Category("SIG")]
|
||||
public string Text
|
||||
{
|
||||
get { EnsureChildControls(); return _textBox.Text; }
|
||||
set { EnsureChildControls(); _textBox.Text = value; }
|
||||
}
|
||||
|
||||
[Category("SIG")]
|
||||
public string SelectedValue
|
||||
{
|
||||
get { EnsureChildControls(); return _value.Value; }
|
||||
set { EnsureChildControls(); _value.Value = value; }
|
||||
}
|
||||
|
||||
protected override void CreateChildControls()
|
||||
{
|
||||
Controls.Clear();
|
||||
_textBox = new TextBox { ID = "Txt", CssClass = "form-control sig-buscador-input" };
|
||||
_value = new HiddenField { ID = "Hdn" };
|
||||
_button = new Button { ID = "Btn", Text = "...", CssClass = "btn btn-default sig-buscador-button" };
|
||||
_button.OnClientClick = "return false;";
|
||||
Controls.Add(_value);
|
||||
Controls.Add(_textBox);
|
||||
Controls.Add(_button);
|
||||
}
|
||||
|
||||
protected override void Render(HtmlTextWriter writer)
|
||||
{
|
||||
EnsureChildControls();
|
||||
writer.AddAttribute(HtmlTextWriterAttribute.Class, "input-group sig-buscador-catalogo");
|
||||
writer.AddAttribute("data-catalogo", Catalogo ?? string.Empty);
|
||||
writer.RenderBeginTag(HtmlTextWriterTag.Div);
|
||||
_textBox.RenderControl(writer);
|
||||
writer.AddAttribute(HtmlTextWriterAttribute.Class, "input-group-btn");
|
||||
writer.RenderBeginTag(HtmlTextWriterTag.Span);
|
||||
_button.RenderControl(writer);
|
||||
writer.RenderEndTag();
|
||||
_value.RenderControl(writer);
|
||||
writer.RenderEndTag();
|
||||
}
|
||||
|
||||
public static string SearchJson(string json)
|
||||
{
|
||||
var request = JsonConvert.DeserializeObject<BuscadorCatalogoCallbackSolicitud>(json ?? "{}") ?? new BuscadorCatalogoCallbackSolicitud();
|
||||
var user = AppServices.UserContext.GetCurrentUser();
|
||||
var response = AppServices.SearchProvider.Search(request, user);
|
||||
return JsonConvert.SerializeObject(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
82
SIG.Web/AppCode/SigControls/DropdownGrid.cs
Normal file
82
SIG.Web/AppCode/SigControls/DropdownGrid.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
namespace SIG.Web.AppCode.SigControls
|
||||
{
|
||||
public sealed class DropdownGridColumn
|
||||
{
|
||||
public string DataField { get; set; }
|
||||
public string HeaderText { get; set; }
|
||||
public bool Visible { get; set; } = true;
|
||||
}
|
||||
|
||||
[ToolboxData("<{0}:DropdownGrid runat=server></{0}:DropdownGrid>")]
|
||||
public class DropdownGrid : CompositeControl, INamingContainer
|
||||
{
|
||||
private HiddenField _selectedValues;
|
||||
private TextBox _text;
|
||||
|
||||
public List<DropdownGridColumn> Columns
|
||||
{
|
||||
get { return ViewState["Columns"] as List<DropdownGridColumn> ?? new List<DropdownGridColumn>(); }
|
||||
set { ViewState["Columns"] = value; }
|
||||
}
|
||||
|
||||
[Category("SIG")]
|
||||
public bool Multiple
|
||||
{
|
||||
get { return ViewState["Multiple"] as bool? ?? false; }
|
||||
set { ViewState["Multiple"] = value; }
|
||||
}
|
||||
|
||||
[Category("SIG")]
|
||||
public string SelectedValues
|
||||
{
|
||||
get { EnsureChildControls(); return _selectedValues.Value; }
|
||||
set { EnsureChildControls(); _selectedValues.Value = value; }
|
||||
}
|
||||
|
||||
[Category("SIG")]
|
||||
public string Text
|
||||
{
|
||||
get { EnsureChildControls(); return _text.Text; }
|
||||
set { EnsureChildControls(); _text.Text = value; }
|
||||
}
|
||||
|
||||
protected override void CreateChildControls()
|
||||
{
|
||||
Controls.Clear();
|
||||
_selectedValues = new HiddenField { ID = "HdnValues" };
|
||||
_text = new TextBox { ID = "TxtText", CssClass = "form-control sig-dropdown-grid-text" };
|
||||
Controls.Add(_selectedValues);
|
||||
Controls.Add(_text);
|
||||
}
|
||||
|
||||
protected override void Render(HtmlTextWriter writer)
|
||||
{
|
||||
EnsureChildControls();
|
||||
writer.AddAttribute(HtmlTextWriterAttribute.Class, "sig-dropdown-grid");
|
||||
writer.AddAttribute("data-multiple", Multiple ? "true" : "false");
|
||||
writer.RenderBeginTag(HtmlTextWriterTag.Div);
|
||||
_selectedValues.RenderControl(writer);
|
||||
_text.RenderControl(writer);
|
||||
writer.AddAttribute(HtmlTextWriterAttribute.Class, "sig-dropdown-grid-popup");
|
||||
writer.RenderBeginTag(HtmlTextWriterTag.Div);
|
||||
writer.Write("<!-- grid popup rendered by JavaScript -->");
|
||||
writer.RenderEndTag();
|
||||
writer.RenderEndTag();
|
||||
}
|
||||
|
||||
public IReadOnlyList<string> GetSelectedValueList()
|
||||
{
|
||||
return (SelectedValues ?? string.Empty)
|
||||
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => x.Trim())
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
14
SIG.Web/AppCode/SigControls/FiltroActivo.cs
Normal file
14
SIG.Web/AppCode/SigControls/FiltroActivo.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace SIG.Web.AppCode.SigControls
|
||||
{
|
||||
[Serializable]
|
||||
public sealed class FiltroActivo
|
||||
{
|
||||
public string Nombre { get; set; }
|
||||
public string Titulo { get; set; }
|
||||
public string Descripcion { get; set; }
|
||||
public int Orden { get; set; }
|
||||
public bool Visible { get; set; } = true;
|
||||
}
|
||||
}
|
||||
9
SIG.Web/AppCode/SigControls/PDFViewer/PdfClientAction.cs
Normal file
9
SIG.Web/AppCode/SigControls/PDFViewer/PdfClientAction.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace SIG.Web.AppCode.SigControls.PDFViewer
|
||||
{
|
||||
public sealed class PdfClientAction
|
||||
{
|
||||
public string Action { get; set; }
|
||||
public string Message { get; set; }
|
||||
public string Url { get; set; }
|
||||
}
|
||||
}
|
||||
10
SIG.Web/AppCode/SigControls/PDFViewer/PdfDocumentComposer.cs
Normal file
10
SIG.Web/AppCode/SigControls/PDFViewer/PdfDocumentComposer.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace SIG.Web.AppCode.SigControls.PDFViewer
|
||||
{
|
||||
public sealed class PdfDocumentComposer
|
||||
{
|
||||
public byte[] Compose(byte[] originalPdf)
|
||||
{
|
||||
return originalPdf ?? new byte[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace SIG.Web.AppCode.SigControls.PDFViewer
|
||||
{
|
||||
public sealed class PdfSignatureAsset
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public byte[] Bytes { get; set; }
|
||||
public string ContentType { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SIG.Web.AppCode.SigControls.PDFViewer
|
||||
{
|
||||
public sealed class PdfSignatureComposer
|
||||
{
|
||||
public byte[] ApplySignatures(byte[] originalPdf, IEnumerable<PdfSignaturePlacement> placements)
|
||||
{
|
||||
// Placeholder. Keep signature composition behind this class.
|
||||
return originalPdf ?? new byte[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace SIG.Web.AppCode.SigControls.PDFViewer
|
||||
{
|
||||
public sealed class PdfSignaturePlacement
|
||||
{
|
||||
public int Page { get; set; }
|
||||
public float X { get; set; }
|
||||
public float Y { get; set; }
|
||||
public float Width { get; set; }
|
||||
public float Height { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace SIG.Web.AppCode.SigControls.PDFViewer
|
||||
{
|
||||
public sealed class PdfSignatureSavingEventArgs : EventArgs
|
||||
{
|
||||
public PdfSignatureAsset Signature { get; }
|
||||
public bool Cancel { get; set; }
|
||||
|
||||
public PdfSignatureSavingEventArgs(PdfSignatureAsset signature)
|
||||
{
|
||||
Signature = signature;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace SIG.Web.AppCode.SigControls.PDFViewer
|
||||
{
|
||||
public sealed class PdfSignedDocumentEventArgs : EventArgs
|
||||
{
|
||||
public byte[] Bytes { get; }
|
||||
public string FileName { get; }
|
||||
|
||||
public PdfSignedDocumentEventArgs(byte[] bytes, string fileName)
|
||||
{
|
||||
Bytes = bytes;
|
||||
FileName = fileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
SIG.Web/AppCode/SigControls/PDFViewer/PdfSourceMode.cs
Normal file
11
SIG.Web/AppCode/SigControls/PDFViewer/PdfSourceMode.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace SIG.Web.AppCode.SigControls.PDFViewer
|
||||
{
|
||||
public enum PdfSourceMode
|
||||
{
|
||||
None,
|
||||
Bytes,
|
||||
FilePath,
|
||||
Url,
|
||||
TempStorage
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace SIG.Web.AppCode.SigControls.PDFViewer
|
||||
{
|
||||
public sealed class PdfStoredSignature
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string DataUrl { get; set; }
|
||||
}
|
||||
}
|
||||
29
SIG.Web/AppCode/SigControls/PDFViewer/PdfTempStorage.cs
Normal file
29
SIG.Web/AppCode/SigControls/PDFViewer/PdfTempStorage.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Web;
|
||||
|
||||
namespace SIG.Web.AppCode.SigControls.PDFViewer
|
||||
{
|
||||
public static class PdfTempStorage
|
||||
{
|
||||
private const string Folder = "~/App_Data/PdfTemp";
|
||||
|
||||
public static string Save(byte[] bytes, string extension = ".pdf")
|
||||
{
|
||||
if (bytes == null) throw new ArgumentNullException(nameof(bytes));
|
||||
var dir = HttpContext.Current.Server.MapPath(Folder);
|
||||
Directory.CreateDirectory(dir);
|
||||
var fileName = Guid.NewGuid().ToString("N") + extension;
|
||||
var path = Path.Combine(dir, fileName);
|
||||
File.WriteAllBytes(path, bytes);
|
||||
return path;
|
||||
}
|
||||
|
||||
public static byte[] Read(string path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path) || !File.Exists(path))
|
||||
return null;
|
||||
return File.ReadAllBytes(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace SIG.Web.AppCode.SigControls.PDFViewer
|
||||
{
|
||||
public sealed class PdfUploadedDocument
|
||||
{
|
||||
public string FileName { get; set; }
|
||||
public byte[] Bytes { get; set; }
|
||||
}
|
||||
}
|
||||
24
SIG.Web/AppCode/SigControls/PDFViewer/PdfViewerHandler.cs
Normal file
24
SIG.Web/AppCode/SigControls/PDFViewer/PdfViewerHandler.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System.Web;
|
||||
using System.Web.SessionState;
|
||||
|
||||
namespace SIG.Web.AppCode.SigControls.PDFViewer
|
||||
{
|
||||
public sealed class PdfViewerHandler : IHttpHandler, IRequiresSessionState
|
||||
{
|
||||
public bool IsReusable => false;
|
||||
|
||||
public void ProcessRequest(HttpContext context)
|
||||
{
|
||||
var path = context.Request.QueryString["path"];
|
||||
var bytes = PdfTempStorage.Read(path);
|
||||
if (bytes == null)
|
||||
{
|
||||
context.Response.StatusCode = 404;
|
||||
return;
|
||||
}
|
||||
|
||||
context.Response.ContentType = "application/pdf";
|
||||
context.Response.BinaryWrite(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
15
SIG.Web/AppCode/SigControls/PDFViewer/PdfViewerState.cs
Normal file
15
SIG.Web/AppCode/SigControls/PDFViewer/PdfViewerState.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SIG.Web.AppCode.SigControls.PDFViewer
|
||||
{
|
||||
[Serializable]
|
||||
public sealed class PdfViewerState
|
||||
{
|
||||
public string DocumentId { get; set; }
|
||||
public PdfSourceMode SourceMode { get; set; }
|
||||
public string FileName { get; set; }
|
||||
public string TempPath { get; set; }
|
||||
public List<PdfSignaturePlacement> Signatures { get; set; } = new List<PdfSignaturePlacement>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.IO;
|
||||
using System.Web;
|
||||
using System.Web.Script.Serialization;
|
||||
using System.Web.SessionState;
|
||||
|
||||
namespace SIG.Web.AppCode.SigControls.PDFViewer
|
||||
{
|
||||
public sealed class PdfViewerUploadHandler : IHttpHandler, IRequiresSessionState
|
||||
{
|
||||
public bool IsReusable => false;
|
||||
|
||||
public void ProcessRequest(HttpContext context)
|
||||
{
|
||||
if (context.Request.Files.Count == 0)
|
||||
{
|
||||
WriteJson(context, new { success = false, message = "No file received." });
|
||||
return;
|
||||
}
|
||||
|
||||
var file = context.Request.Files[0];
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
file.InputStream.CopyTo(ms);
|
||||
var path = PdfTempStorage.Save(ms.ToArray());
|
||||
WriteJson(context, new { success = true, path, fileName = file.FileName });
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteJson(HttpContext context, object payload)
|
||||
{
|
||||
context.Response.ContentType = "application/json";
|
||||
context.Response.Write(new JavaScriptSerializer().Serialize(payload));
|
||||
}
|
||||
}
|
||||
}
|
||||
91
SIG.Web/AppCode/SigControls/PDFViewer/SigPdfViewer.cs
Normal file
91
SIG.Web/AppCode/SigControls/PDFViewer/SigPdfViewer.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Net;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
namespace SIG.Web.AppCode.SigControls.PDFViewer
|
||||
{
|
||||
[ToolboxData("<{0}:SigPdfViewer runat=server></{0}:SigPdfViewer>")]
|
||||
public class SigPdfViewer : CompositeControl, INamingContainer
|
||||
{
|
||||
private HiddenField _path;
|
||||
|
||||
[Category("SIG")]
|
||||
public string DocumentPath
|
||||
{
|
||||
get { EnsureChildControls(); return _path.Value; }
|
||||
set { EnsureChildControls(); _path.Value = value; }
|
||||
}
|
||||
|
||||
[Category("SIG")]
|
||||
public bool ShowSignaturePanel
|
||||
{
|
||||
get { return ViewState["ShowSignaturePanel"] as bool? ?? false; }
|
||||
set { ViewState["ShowSignaturePanel"] = value; }
|
||||
}
|
||||
|
||||
public void LoadFromBytes(byte[] pdfBytes, string fileName = "document.pdf")
|
||||
{
|
||||
DocumentPath = PdfTempStorage.Save(pdfBytes);
|
||||
}
|
||||
|
||||
public void LoadFromServerPath(string physicalPath)
|
||||
{
|
||||
DocumentPath = physicalPath;
|
||||
}
|
||||
|
||||
public void LoadFromUrl(string url)
|
||||
{
|
||||
using (var client = new WebClient())
|
||||
{
|
||||
LoadFromBytes(client.DownloadData(url));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void CreateChildControls()
|
||||
{
|
||||
Controls.Clear();
|
||||
_path = new HiddenField { ID = "HdnPdfPath" };
|
||||
Controls.Add(_path);
|
||||
}
|
||||
|
||||
protected override void Render(HtmlTextWriter writer)
|
||||
{
|
||||
EnsureChildControls();
|
||||
writer.AddAttribute(HtmlTextWriterAttribute.Class, "sig-pdf-control");
|
||||
writer.RenderBeginTag(HtmlTextWriterTag.Div);
|
||||
_path.RenderControl(writer);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(DocumentPath))
|
||||
{
|
||||
writer.AddAttribute(HtmlTextWriterAttribute.Class, "alert alert-info");
|
||||
writer.RenderBeginTag(HtmlTextWriterTag.Div);
|
||||
writer.WriteEncodedText("No PDF loaded.");
|
||||
writer.RenderEndTag();
|
||||
}
|
||||
else
|
||||
{
|
||||
var url = "SigPdfViewerHandler.ashx?path=" + Page.Server.UrlEncode(DocumentPath);
|
||||
writer.AddAttribute(HtmlTextWriterAttribute.Class, "sig-pdf-toolbar");
|
||||
writer.RenderBeginTag(HtmlTextWriterTag.Div);
|
||||
writer.AddAttribute(HtmlTextWriterAttribute.Href, url);
|
||||
writer.AddAttribute(HtmlTextWriterAttribute.Target, "_blank");
|
||||
writer.AddAttribute(HtmlTextWriterAttribute.Class, "btn btn-default");
|
||||
writer.RenderBeginTag(HtmlTextWriterTag.A);
|
||||
writer.Write("Open PDF");
|
||||
writer.RenderEndTag();
|
||||
writer.RenderEndTag();
|
||||
|
||||
writer.AddAttribute(HtmlTextWriterAttribute.Src, url);
|
||||
writer.AddAttribute(HtmlTextWriterAttribute.Class, "sig-pdf-frame");
|
||||
writer.AddStyleAttribute(HtmlTextWriterStyle.Width, "100%");
|
||||
writer.AddStyleAttribute(HtmlTextWriterStyle.Height, "700px");
|
||||
writer.RenderBeginTag(HtmlTextWriterTag.Iframe);
|
||||
writer.RenderEndTag();
|
||||
}
|
||||
|
||||
writer.RenderEndTag();
|
||||
}
|
||||
}
|
||||
}
|
||||
66
SIG.Web/AppCode/SigControls/PorcentajeBox.cs
Normal file
66
SIG.Web/AppCode/SigControls/PorcentajeBox.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
namespace SIG.Web.AppCode.SigControls
|
||||
{
|
||||
[ToolboxData("<{0}:PorcentajeBox runat=server></{0}:PorcentajeBox>")]
|
||||
public class PorcentajeBox : WebControl, IPostBackDataHandler
|
||||
{
|
||||
[Category("SIG")]
|
||||
public decimal Value
|
||||
{
|
||||
get { return ViewState["Value"] as decimal? ?? 0m; }
|
||||
set { ViewState["Value"] = Math.Max(0m, Math.Min(100m, value)); }
|
||||
}
|
||||
|
||||
[Category("SIG")]
|
||||
public string HiddenFieldName => UniqueID + "$value";
|
||||
|
||||
protected override void RenderContents(HtmlTextWriter writer)
|
||||
{
|
||||
var value = Value.ToString("0.##", CultureInfo.InvariantCulture);
|
||||
writer.AddAttribute(HtmlTextWriterAttribute.Class, "sig-porcentaje-box");
|
||||
writer.RenderBeginTag(HtmlTextWriterTag.Div);
|
||||
|
||||
writer.AddAttribute(HtmlTextWriterAttribute.Type, "hidden");
|
||||
writer.AddAttribute(HtmlTextWriterAttribute.Name, HiddenFieldName);
|
||||
writer.AddAttribute(HtmlTextWriterAttribute.Value, value);
|
||||
writer.RenderBeginTag(HtmlTextWriterTag.Input);
|
||||
writer.RenderEndTag();
|
||||
|
||||
writer.AddAttribute(HtmlTextWriterAttribute.Class, "sig-porcentaje-track");
|
||||
writer.RenderBeginTag(HtmlTextWriterTag.Div);
|
||||
writer.AddStyleAttribute(HtmlTextWriterStyle.Width, value + "%");
|
||||
writer.AddAttribute(HtmlTextWriterAttribute.Class, "sig-porcentaje-fill");
|
||||
writer.RenderBeginTag(HtmlTextWriterTag.Div);
|
||||
writer.RenderEndTag();
|
||||
writer.RenderEndTag();
|
||||
|
||||
writer.AddAttribute(HtmlTextWriterAttribute.Class, "sig-porcentaje-text");
|
||||
writer.RenderBeginTag(HtmlTextWriterTag.Span);
|
||||
writer.WriteEncodedText(value + "%");
|
||||
writer.RenderEndTag();
|
||||
|
||||
writer.RenderEndTag();
|
||||
}
|
||||
|
||||
public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
|
||||
{
|
||||
var posted = postCollection[HiddenFieldName];
|
||||
decimal newValue;
|
||||
if (!decimal.TryParse(posted, NumberStyles.Any, CultureInfo.InvariantCulture, out newValue))
|
||||
return false;
|
||||
|
||||
if (newValue == Value) return false;
|
||||
Value = newValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void RaisePostDataChangedEvent()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
118
SIG.Web/AppCode/Utilities/DataReaderAsyncExtensions.cs
Normal file
118
SIG.Web/AppCode/Utilities/DataReaderAsyncExtensions.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace SIG.Web.AppCode.Utilities
|
||||
{
|
||||
public static partial class DataReaderExtensions
|
||||
{
|
||||
public static async Task<T> ToObjectAsync<T>(
|
||||
this DbDataReader reader,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
where T : class, new()
|
||||
{
|
||||
if (reader == null)
|
||||
{
|
||||
throw new ArgumentNullException("reader");
|
||||
}
|
||||
|
||||
bool hasRow = await reader.ReadAsync(cancellationToken).ConfigureAwait(false);
|
||||
return hasRow ? MapCurrentRow<T>(reader) : null;
|
||||
}
|
||||
|
||||
public static async Task<List<T>> ToListAsync<T>(
|
||||
this DbDataReader reader,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
where T : class, new()
|
||||
{
|
||||
if (reader == null)
|
||||
{
|
||||
throw new ArgumentNullException("reader");
|
||||
}
|
||||
|
||||
var result = new List<T>();
|
||||
|
||||
while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
result.Add(MapCurrentRow<T>(reader));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static async Task<DataTable> ToDataTableAsync(
|
||||
this DbDataReader reader,
|
||||
string tableName = null,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (reader == null)
|
||||
{
|
||||
throw new ArgumentNullException("reader");
|
||||
}
|
||||
|
||||
var result = string.IsNullOrWhiteSpace(tableName)
|
||||
? new DataTable()
|
||||
: new DataTable(tableName);
|
||||
|
||||
result.Locale = CultureInfo.InvariantCulture;
|
||||
|
||||
for (int index = 0; index < reader.FieldCount; index++)
|
||||
{
|
||||
string columnName = reader.GetName(index);
|
||||
Type columnType = reader.GetFieldType(index) ?? typeof(object);
|
||||
|
||||
if (result.Columns.Contains(columnName))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"El resultado contiene una columna duplicada: " + columnName + ".");
|
||||
}
|
||||
|
||||
result.Columns.Add(columnName, columnType);
|
||||
}
|
||||
|
||||
while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
var values = new object[reader.FieldCount];
|
||||
reader.GetValues(values);
|
||||
result.Rows.Add(values);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static async Task<JArray> ToJsonArrayAsync(
|
||||
this DbDataReader reader,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (reader == null)
|
||||
{
|
||||
throw new ArgumentNullException("reader");
|
||||
}
|
||||
|
||||
var result = new JArray();
|
||||
|
||||
while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
var item = new JObject();
|
||||
|
||||
for (int index = 0; index < reader.FieldCount; index++)
|
||||
{
|
||||
string columnName = reader.GetName(index);
|
||||
object value = reader.GetValue(index);
|
||||
item[columnName] = value == null || value == DBNull.Value
|
||||
? JValue.CreateNull()
|
||||
: JToken.FromObject(value);
|
||||
}
|
||||
|
||||
result.Add(item);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
198
SIG.Web/AppCode/Utilities/DataReaderExtensions.cs
Normal file
198
SIG.Web/AppCode/Utilities/DataReaderExtensions.cs
Normal file
@@ -0,0 +1,198 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Data;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace SIG.Web.AppCode.Utilities
|
||||
{
|
||||
public static partial class DataReaderExtensions
|
||||
{
|
||||
private static readonly ConcurrentDictionary<Type, PropertyMap[]> PropertyMaps =
|
||||
new ConcurrentDictionary<Type, PropertyMap[]>();
|
||||
|
||||
public static T ToObject<T>(this IDataReader reader) where T : class, new()
|
||||
{
|
||||
if (reader == null)
|
||||
{
|
||||
throw new ArgumentNullException("reader");
|
||||
}
|
||||
|
||||
return reader.Read() ? MapCurrentRow<T>(reader) : null;
|
||||
}
|
||||
|
||||
public static List<T> ToList<T>(this IDataReader reader) where T : class, new()
|
||||
{
|
||||
if (reader == null)
|
||||
{
|
||||
throw new ArgumentNullException("reader");
|
||||
}
|
||||
|
||||
var result = new List<T>();
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
result.Add(MapCurrentRow<T>(reader));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static DataTable ToDataTable(this IDataReader reader, string tableName = null)
|
||||
{
|
||||
if (reader == null)
|
||||
{
|
||||
throw new ArgumentNullException("reader");
|
||||
}
|
||||
|
||||
var result = string.IsNullOrWhiteSpace(tableName)
|
||||
? new DataTable()
|
||||
: new DataTable(tableName);
|
||||
|
||||
result.Locale = CultureInfo.InvariantCulture;
|
||||
result.Load(reader);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static JArray ToJsonArray(this IDataReader reader)
|
||||
{
|
||||
if (reader == null)
|
||||
{
|
||||
throw new ArgumentNullException("reader");
|
||||
}
|
||||
|
||||
var result = new JArray();
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
var item = new JObject();
|
||||
|
||||
for (int index = 0; index < reader.FieldCount; index++)
|
||||
{
|
||||
string columnName = reader.GetName(index);
|
||||
object value = reader.GetValue(index);
|
||||
item[columnName] = value == null || value == DBNull.Value
|
||||
? JValue.CreateNull()
|
||||
: JToken.FromObject(value);
|
||||
}
|
||||
|
||||
result.Add(item);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static T MapCurrentRow<T>(IDataRecord record) where T : class, new()
|
||||
{
|
||||
T item = new T();
|
||||
IDictionary<string, int> columns = GetColumns(record);
|
||||
|
||||
foreach (PropertyMap map in GetPropertyMaps(typeof(T)))
|
||||
{
|
||||
int ordinal;
|
||||
|
||||
if (!columns.TryGetValue(map.ColumnName, out ordinal) || record.IsDBNull(ordinal))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
object convertedValue = ConvertValue(record.GetValue(ordinal), map.Property.PropertyType);
|
||||
map.Property.SetValue(item, convertedValue, null);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private static IDictionary<string, int> GetColumns(IDataRecord record)
|
||||
{
|
||||
var columns = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
for (int index = 0; index < record.FieldCount; index++)
|
||||
{
|
||||
string name = record.GetName(index);
|
||||
|
||||
if (!columns.ContainsKey(name))
|
||||
{
|
||||
columns.Add(name, index);
|
||||
}
|
||||
}
|
||||
|
||||
return columns;
|
||||
}
|
||||
|
||||
private static PropertyMap[] GetPropertyMaps(Type type)
|
||||
{
|
||||
return PropertyMaps.GetOrAdd(type, currentType =>
|
||||
currentType
|
||||
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(property => property.CanWrite && property.GetIndexParameters().Length == 0)
|
||||
.Select(property =>
|
||||
{
|
||||
ColumnAttribute column = property.GetCustomAttributes(typeof(ColumnAttribute), true)
|
||||
.OfType<ColumnAttribute>()
|
||||
.FirstOrDefault();
|
||||
|
||||
return new PropertyMap
|
||||
{
|
||||
ColumnName = column == null || string.IsNullOrWhiteSpace(column.Name)
|
||||
? property.Name
|
||||
: column.Name,
|
||||
Property = property
|
||||
};
|
||||
})
|
||||
.ToArray());
|
||||
}
|
||||
|
||||
private static object ConvertValue(object value, Type destinationType)
|
||||
{
|
||||
Type targetType = Nullable.GetUnderlyingType(destinationType) ?? destinationType;
|
||||
|
||||
if (targetType.IsInstanceOfType(value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
if (targetType.IsEnum)
|
||||
{
|
||||
return value is string
|
||||
? Enum.Parse(targetType, Convert.ToString(value, CultureInfo.InvariantCulture), true)
|
||||
: Enum.ToObject(targetType, value);
|
||||
}
|
||||
|
||||
if (targetType == typeof(Guid))
|
||||
{
|
||||
return value is Guid ? value : Guid.Parse(Convert.ToString(value, CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
if (targetType == typeof(TimeSpan))
|
||||
{
|
||||
return value is TimeSpan ? value : TimeSpan.Parse(Convert.ToString(value, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
if (targetType == typeof(DateTimeOffset))
|
||||
{
|
||||
return value is DateTimeOffset
|
||||
? value
|
||||
: DateTimeOffset.Parse(Convert.ToString(value, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
if (targetType == typeof(bool) && value is string)
|
||||
{
|
||||
string booleanValue = Convert.ToString(value, CultureInfo.InvariantCulture);
|
||||
return booleanValue == "1" || booleanValue.Equals("true", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
return Convert.ChangeType(value, targetType, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
private sealed class PropertyMap
|
||||
{
|
||||
public string ColumnName { get; set; }
|
||||
public PropertyInfo Property { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
93
SIG.Web/App_Data/PDFSamples/sample.pdf
Normal file
93
SIG.Web/App_Data/PDFSamples/sample.pdf
Normal file
@@ -0,0 +1,93 @@
|
||||
%PDF-1.4
|
||||
%<25><><EFBFBD><EFBFBD> ReportLab Generated PDF document (opensource)
|
||||
1 0 obj
|
||||
<<
|
||||
/F1 2 0 R /F2 3 0 R
|
||||
>>
|
||||
endobj
|
||||
2 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
3 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
4 0 obj
|
||||
<<
|
||||
/Contents 9 0 R /MediaBox [ 0 0 612 792 ] /Parent 8 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
5 0 obj
|
||||
<<
|
||||
/Contents 10 0 R /MediaBox [ 0 0 612 792 ] /Parent 8 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
6 0 obj
|
||||
<<
|
||||
/PageMode /UseNone /Pages 8 0 R /Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
7 0 obj
|
||||
<<
|
||||
/Author (\(anonymous\)) /CreationDate (D:20260706205140+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260706205140+00'00') /Producer (ReportLab PDF Library - \(opensource\))
|
||||
/Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False
|
||||
>>
|
||||
endobj
|
||||
8 0 obj
|
||||
<<
|
||||
/Count 2 /Kids [ 4 0 R 5 0 R ] /Type /Pages
|
||||
>>
|
||||
endobj
|
||||
9 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 955
|
||||
>>
|
||||
stream
|
||||
Gat%!gN)"%&:N^lp+agB@L6B`dp+r_a`N^([*Jd]m#5a@[Xc`.mn1#d6\%qP",PKBFa!ML2$FL4C;1Q2^t0:'mO:s:kn9pjHq5@;/fhWbB^Zfm&jo&3_XIQZ4_nt:,<SR_U>`Wq,;GjrM8Yd<'*X&D%!U///fns"iFa=BU4HOV=I5c1>SjDl$-[Ws9*+8H"k$IC04?UuMK#0V3Ihej4f$CciBP-+*\HOAft8ojM_m(jR'3PaM@?d3,G8o0U-U<VU=GoX!AiO;?B4"+q-a\S^2&W1QdCV%KA"d[k@<FM"q+RAKZUO8jM\Z)Z3=7=,Y^kAM@eXqn&3mTjQe0`nr60G!DQh5KR$G]:OrX?LasTfa<Ei&'r]?-.<[0I1"q;7cj3`[@OUh-#LEG25a'KT]F[UHZ+s0)2tj*TF%>_cZQTGE^Cel^A44UB[b!Ghf:]`1:c^K$]pPdAZOidR[2;kdi(CZ_lfQuPjh,AHh^QW7WN&uX*8<X_0Vm2AKasfp;2<*<jW'*5nJ]K0]<nK&@GX?$7E<+;%UiI?M>(1V6KX82YAtkW)c^T_)U^%kUX:-MOP2dXZfdGB?5Sp>[TNL;23Qg$%(r_ho%[pPWm6p?f6.n#mu.fQhE)1XLe]9d+6O4-r[G:M=&dA06LO2oEau+WEe_,dC28Al>[Ti9V)0`gbYV-6^a(i3N>3d@\#e1b2!<5-#OYI+c[oe!hMp0\BNpQ*Z]VjSW!:KF9.^n)&9F,EQ:PlE7VprSm*RY5LrsE+4+:L+,jO#Xia05OrhCW!IiFLM[NE@<HW?H@KUK7J[I2e^JX>+UG2DH]_iAMF7o;?(/tE`Rm&[^=<YVO>ij,oEfkdgu$[:X[Z+9"hSE#'_S=ej.kSSe`Ke<>%HeE)/jID]HW;*5lTq'0m>:CQLbG%p_buXtT7:t\*UU9\J9$>_mZ9?b#c/@FaLN=A'IfRU^fh2~>endstream
|
||||
endobj
|
||||
10 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 401
|
||||
>>
|
||||
stream
|
||||
GasIa9i&Vk&A@g>]H\>oC]fuL1s1Z-+4^8:*U"-I>BdO^r-F-M(_JTg9JTsnhKo;e%ajF5!q()6Ns%k&Hj\8^qoHg&aJ8K#lo,/@4@C[I38ju-pY*?N7=6QpBREl9;?AT.+sk>H7q?<bL4(-+$Cqt_XAPqXN%U4<O^Fj[@tWZ&E?)[+anDof:X\TJ;TXq5bNlS,>#Lp(e/nUY%$ouH<0Q3>7P=@-#&OgV5q!%.j+FK'GX%)K*_T3LjPhg!B1NQ=F/5"L8;mI=f'H`'FBHFXV2Z[C$Y<a_!3L#%$&rrcK@<73`UY"L_Gi1E<JAl=3_UI*:lshhC=d5iG^UX.G':8WaQpL\KiTtU%P+Vr\q8NQcE[bQFaDM_`?j:'D;tu?`&:(Va-Ie$K]j)B@&qf~>endstream
|
||||
endobj
|
||||
xref
|
||||
0 11
|
||||
0000000000 65535 f
|
||||
0000000061 00000 n
|
||||
0000000102 00000 n
|
||||
0000000209 00000 n
|
||||
0000000321 00000 n
|
||||
0000000514 00000 n
|
||||
0000000708 00000 n
|
||||
0000000776 00000 n
|
||||
0000001056 00000 n
|
||||
0000001121 00000 n
|
||||
0000002166 00000 n
|
||||
trailer
|
||||
<<
|
||||
/ID
|
||||
[<11cd4642e6f5272b0b781904ee9674eb><11cd4642e6f5272b0b781904ee9674eb>]
|
||||
% ReportLab generated PDF document -- digest (opensource)
|
||||
|
||||
/Info 7 0 R
|
||||
/Root 6 0 R
|
||||
/Size 11
|
||||
>>
|
||||
startxref
|
||||
2658
|
||||
%%EOF
|
||||
1
SIG.Web/Content/AjaxControlToolkit/Styles/.keep
Normal file
1
SIG.Web/Content/AjaxControlToolkit/Styles/.keep
Normal file
@@ -0,0 +1 @@
|
||||
Copy files here.
|
||||
1
SIG.Web/Global.asax
Normal file
1
SIG.Web/Global.asax
Normal file
@@ -0,0 +1 @@
|
||||
<%@ Application Codebehind="Global.asax.cs" Inherits="SIG.Web.Global" Language="C#" %>
|
||||
22
SIG.Web/Global.asax.cs
Normal file
22
SIG.Web/Global.asax.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Web;
|
||||
using System.Web.Optimization;
|
||||
using SIG.Web.AppCode.Configuration;
|
||||
|
||||
namespace SIG.Web
|
||||
{
|
||||
public class Global : HttpApplication
|
||||
{
|
||||
protected void Application_Start(object sender, EventArgs e)
|
||||
{
|
||||
AppServices.Configure();
|
||||
BundleConfig.RegisterBundles(BundleTable.Bundles);
|
||||
ErrorHandlerConfig.Register();
|
||||
}
|
||||
|
||||
protected void Application_Error(object sender, EventArgs e)
|
||||
{
|
||||
ErrorHandlerConfig.HandleServerError(Server.GetLastError(), Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
16
SIG.Web/Imagenes/README.txt
Normal file
16
SIG.Web/Imagenes/README.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
Archivos sugeridos para la carpeta ~/Imagenes/
|
||||
|
||||
- login-fondo-cerro-campana.jpg
|
||||
Fondo sugerido para el login.
|
||||
|
||||
- logo-agua-hermosillo-blanco.png
|
||||
Logo blanco con fondo transparente (extraido del mockup).
|
||||
|
||||
- icono-desarrolladores-azul.png
|
||||
Icono de casa/desarrolladores azul con fondo transparente (extraido del mockup).
|
||||
|
||||
Archivos extra de referencia:
|
||||
- login-preview-completo.png
|
||||
- cerro-campana-referencia.jpg
|
||||
- logo-agua-hermosillo-blanco_recorte.png
|
||||
- icono-desarrolladores-azul_recorte.png
|
||||
BIN
SIG.Web/Imagenes/cerro-campana-referencia.jpg
Normal file
BIN
SIG.Web/Imagenes/cerro-campana-referencia.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 103 KiB |
BIN
SIG.Web/Imagenes/icono-desarrolladores-azul.png
Normal file
BIN
SIG.Web/Imagenes/icono-desarrolladores-azul.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.4 KiB |
BIN
SIG.Web/Imagenes/icono-desarrolladores-azul_recorte.png
Normal file
BIN
SIG.Web/Imagenes/icono-desarrolladores-azul_recorte.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
BIN
SIG.Web/Imagenes/login-fondo-cerro-campana.jpg
Normal file
BIN
SIG.Web/Imagenes/login-fondo-cerro-campana.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.4 MiB |
BIN
SIG.Web/Imagenes/login-preview-completo.png
Normal file
BIN
SIG.Web/Imagenes/login-preview-completo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 MiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user