3.0.1.8 Control repository status check on update

This commit is contained in:
Andy Howard
2025-10-26 14:51:00 +00:00
parent a841bd8b4b
commit d414910e96
47 changed files with 727 additions and 404 deletions
@@ -26,6 +26,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
namespace Sarand.BusinessMgr.Web.Control.App
{
@@ -52,19 +53,53 @@ namespace Sarand.BusinessMgr.Web.Control.App
private ISacsDatabase templateDatabase = null;
// Core Session contexts - to access Core database via repository configuration.
private static ushort sessionContextIdx_Next = 101;
private class CoreInstanceContextData
private class CoreInstanceContextData : SacsDisposable
{
public CoreInstanceContextData(SbmSessionContext_Web sessionContext, SysId controlOwnerId, SysId controlRepositoryId)
public CoreInstanceContextData(SbmSessionContext_Web sessionContext, SbmcRepository repository)
{
SessionContext = sessionContext;
ControlOwnerId = controlOwnerId;
ControlRepositoryId = controlRepositoryId;
ControlRepositoryId = repository.Id;
RefreshRepository(repository);
}
protected override void Dispose(bool disposing)
{
instanceDataCache?.Dispose();
instanceDataCache = null;
}
public void RefreshRepository(SbmcRepository repository)
{
bool contextChanged = false;
if (ControlOwnerId is null || !ControlOwnerId.Equals(repository.OwnerId))
{
ControlOwnerId = repository.OwnerId;
contextChanged = true;
}
if (!RepositoryContexts.Contains(repository.Context))
{
RepositoryContexts.Clear();
RepositoryContexts.Add(repository.Context);
contextChanged = true;
}
// Database.
SacsDatabaseConsole.OdbcConfig odbcConfig = SbmCoreInstanceConsole_Dbas.GetRepositoryOdbcConfig(repository);
if (!odbcConfig.ConnectionString.EqualsCI(OdbcConfig?.ConnectionString ?? ""))
{
OdbcConfig = odbcConfig;
contextChanged = true;
}
if (contextChanged)
{
instanceConfig = null;
instanceDataCache?.Reset();
}
PropertiesPopulated = DateTime.Now;
}
public SbmSessionContext_Web SessionContext { get; }
public SysId ControlOwnerId { get; }
public SysId ControlRepositoryId { get; }
public SysId ControlOwnerId { get; private set; }
public SysId ControlRepositoryId { get; init; }
public HashSet<string> RepositoryContexts { get; } = new HashSet<string>();
public ISacsDatabase BusMgrDatabase { get; set; }
public SacsDatabaseConsole.OdbcConfig OdbcConfig { get; private set; }
public DateTime PropertiesPopulated { get; private set; } = DateTime.Now;
// Instance config.
private SbmInstanceConfig instanceConfig = null;
public SbmInstanceConfig InstanceConfig
@@ -108,9 +143,20 @@ namespace Sarand.BusinessMgr.Web.Control.App
throw;
}
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
foreach (var coreInstance in coreInstances.Values)
coreInstance.Dispose();
coreInstances.Clear();
}
}
//-------------------------------------------------------------------
// Properties.
public static SbmApplication_Control Instance { get { return appInstance; } }
// Application Configuration.
public override Net.Core.Web.App.SbmAppConfig AppConfig { get { return appConfigs.Values.ToList().getConfigForApplication(); } }
@@ -128,12 +174,13 @@ namespace Sarand.BusinessMgr.Web.Control.App
{
return new List<SbmSessionContext>(coreInstances.Values.Select(item => item.SessionContext));
}
/// <summary>
/// Identify the Core session context by the Repository Id.
/// <para>If context not yet defined for Repository, load and then return index.</para>
/// <para>If repository not found, return 0.</para>
/// </summary>
public ushort GetCoreSessionIndexForControlRepository(SysId ownerId, SysId repositoryId)
public ushort GetCoreSessionIndexForControlRepository(SysId repositoryId)
{
using (DisposableCollection<ISacsDisposable> disposables = new DisposableCollection<ISacsDisposable>())
{
@@ -145,19 +192,11 @@ namespace Sarand.BusinessMgr.Web.Control.App
SbmcRepository repository = instanceMgr.GetRepositoryById(repositoryId);
if (repository != null)
{
// Core database connection.
SacsDatabaseConsole.OdbcConfig odbcConfig = SbmCoreInstanceConsole_Dbas.GetRepositoryOdbcConfig(repository);
SacsDatabase busMgrDatabase = SacsDatabaseConsole.OpenOdbcDatabase(odbcConfig);
if (busMgrDatabase.HasErrorState())
SaLogger.Warn($"{SysUtil.Fn} BusMgr Database: " + busMgrDatabase.DatabaseName + ". Initialisation error: " + busMgrDatabase.ErrorStateMessage);
// Session context.
ushort sessionContextIdx = sessionContextIdx_Next++;
if (sessionContextIdx_Control < 1)
sessionContextIdx_Control = sessionContextIdx;
CoreInstanceContextData sessionContextData = new CoreInstanceContextData(new SbmSessionContext_Web(sessionContextIdx, Sbm.Application.WebControl), ownerId, repositoryId);
sessionContextData.RepositoryContexts.Add(repository.Context);
sessionContextData.BusMgrDatabase = busMgrDatabase;
CoreInstanceContextData sessionContextData = new CoreInstanceContextData(new SbmSessionContext_Web(sessionContextIdx, Sbm.Application.WebControl), repository);
coreInstances.Add(sessionContextData.SessionContext.Index, sessionContextData);
return sessionContextData.SessionContext.Index;
}
@@ -165,6 +204,18 @@ namespace Sarand.BusinessMgr.Web.Control.App
return 0;
}
/// <summary>
/// Refresh the session context properties for the specified repository.
/// </summary>
/// <param name="repository"></param>
public void RefreshInstanceSessionProperties(SbmcRepository repository)
{
// Find session context and refresh repository properties.
CoreInstanceContextData sessionContext = coreInstances.Values.FirstOrDefault(item => item.ControlRepositoryId.Equals(repository.Id));
if (sessionContext != null)
sessionContext.RefreshRepository(repository);
}
// Databases.
public override ISacsDatabase GetControlDatabase() { return controlDatabase; }
public override ISacsDatabase GetTemplateDatabase() { return templateDatabase; }
@@ -172,9 +223,12 @@ namespace Sarand.BusinessMgr.Web.Control.App
{
return coreInstances.GetValue(sessionContext.Index, null)?.RepositoryContexts ?? new HashSet<string>();
}
public override ISacsDatabase GetBusMgrDatabase(SbmSessionContext sessionContext)
public override ISacsDatabase CreateBusMgrDatabase(SbmSessionContext sessionContext)
{
return coreInstances.GetValue(sessionContext.Index, null)?.BusMgrDatabase;
ISacsDatabase busMgrDatabase = SacsDatabaseConsole.OpenOdbcDatabase(coreInstances.GetValue(sessionContext.Index, null)?.OdbcConfig);
if (busMgrDatabase.HasErrorState())
SaLogger.Warn($"{SysUtil.Fn} BusMgr Database: " + busMgrDatabase.DatabaseName + ". Initialisation error: " + busMgrDatabase.ErrorStateMessage);
return busMgrDatabase;
}
// Instance configuration.
public override SbmInstanceConfig GetInstanceConfig(SbmSessionContext sessionContext)
@@ -196,7 +250,6 @@ namespace Sarand.BusinessMgr.Web.Control.App
//-------------------------------------------------------------------
// Consoles.
//-------------------------------------------------------------------
public static SbmEmailConsole_Control CreateEmailConsole_Control(SbmEmailConfig emailConfig)
{
@@ -205,6 +258,7 @@ namespace Sarand.BusinessMgr.Web.Control.App
//-------------------------------------------------------------------
// Initialisation.
/// <summary>Initialise environment - from Startup.</summary>
internal void InitWebHostEnvironment(IWebHostEnvironment env)
{
@@ -308,6 +362,7 @@ namespace Sarand.BusinessMgr.Web.Control.App
//-------------------------------------------------------------------
// Generate database definition.
private void GenerateDatabaseDefinition(SbmDbDfnGenerator.DatabaseInstance dbInstance, ISacsDatabase db)
{
if (!SbmSystem.DebugMode) return;
@@ -317,6 +372,7 @@ namespace Sarand.BusinessMgr.Web.Control.App
// Generate database definitions.
GenerateDatabaseDefinition(dbInstance, db.ConnectionString);
}
private void GenerateDatabaseDefinition(SbmDbDfnGenerator.DatabaseInstance dbInstance, String connectionString)
{
if (!SbmSystem.DebugMode) return;
@@ -328,6 +384,22 @@ namespace Sarand.BusinessMgr.Web.Control.App
//SaUpdateLogDbDfnGenerator.Generate(database.ConnectionString);
}
//-------------------------------------------------------------------
// Housekeeping.
private class HousekeepingConfig
{
}
private HousekeepingConfig housekeepingConfig = new HousekeepingConfig();
/// <summary>
/// Perform periodic application housekeeping.
/// </summary>
public override void DoHousekeeping(CancellationToken ct)
{
SaLogger.Info($"{SysUtil.Fn} Doing housekeeping.", true);
}
//-------------------------------------------------------------------
}
//=======================================================================
@@ -1,15 +1,10 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Web.Control.App
{
@@ -28,6 +23,14 @@ namespace Sarand.BusinessMgr.Web.Control.App
// Initialise application.
application = new SbmApplication_Control(Configuration);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
application.Dispose();
}
base.Dispose(disposing);
}
//-------------------------------------------------------------------
// This method gets called by the runtime. Use this method to add services to the container.
@@ -1,20 +1,8 @@
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
using Sarand.BusinessMgr.Net.Standard.Base.Consoles;
using Sarand.BusinessMgr.Net.Standard.Base.Core;
using Sarand.BusinessMgr.Net.Standard.Dbas.Consoles;
using Sarand.BusinessMgr.Web.Control.App;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.Security;
using Sarand.Net.Standard.Base.Utils;
using Sarand.Net.Standard.Dbas.Base;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Sarand.BusinessMgr.Web.Control.Consoles
{
@@ -42,6 +30,8 @@ namespace Sarand.BusinessMgr.Web.Control.Consoles
mbr.OwnerId = repository.OwnerId;
mbr.RepositoryId = repository.Id;
this.SessionData_Control = sessionData_Control;
// In case Repository definition has changed since Instance was registered with Application.
App_Control.RefreshInstanceSessionProperties(repository);
}
protected override void Dispose(bool disposing)
{
@@ -64,7 +54,7 @@ namespace Sarand.BusinessMgr.Web.Control.Consoles
/// <summary>Core internals for accessing core managers etc.</summary>
protected SbmInternals CreateCoreInternals()
{
ushort sessionIndex = App_Control.GetCoreSessionIndexForControlRepository(mbr.OwnerId, mbr.RepositoryId);
ushort sessionIndex = App_Control.GetCoreSessionIndexForControlRepository(mbr.RepositoryId);
SbmSessionContext sessionContext = App_Control.GetSessionContextByIndex(sessionIndex);
return new SbmInternals(sessionContext);
}
@@ -20,7 +20,7 @@ namespace Sarand.BusinessMgr.Web.Control.Controllers.Instance
// Definitions.
// Repository data.
private class RepositoryListData : AjaxResponse_Data.ItemData_Base
internal class RepositoryListData : AjaxResponse_Data.ItemData_Base
{
public String OwnerName { get; set; }
public String Context { get; set; }
@@ -28,12 +28,12 @@ namespace Sarand.BusinessMgr.Web.Control.Controllers.Instance
public String InstanceName { get; set; }
public String DatabaseName { get; set; }
}
private class RepositoryItemData : RepositoryListData
internal class RepositoryItemData : RepositoryListData
{
public String OwnerId { get; set; }
public String UserName { get; set; }
}
private void PopulateRepositoryResponseListData(SbmModel_Control model, RepositoryListData data, SbmcOwner owner, SbmcRepository repository)
internal static void PopulateRepositoryResponseListData(SbmModel_Control model, RepositoryListData data, SbmcOwner owner, SbmcRepository repository)
{
data.Id = SysId.ToString(repository.Id);
data.OwnerName = owner.Name;
@@ -44,7 +44,7 @@ namespace Sarand.BusinessMgr.Web.Control.Controllers.Instance
data.StatusCode = repository.StatusCode;
data.StatusDesc = repository.StatusDesc;
}
private void PopulateRepositoryResponseItemData(SbmModel_Control model, RepositoryItemData data, SbmcOwner owner, SbmcRepository repository)
internal static void PopulateRepositoryResponseItemData(SbmModel_Control model, RepositoryItemData data, SbmcOwner owner, SbmcRepository repository)
{
PopulateRepositoryResponseListData(model, data, owner, repository);
data.OwnerId = SysId.ToString(repository.OwnerId);
@@ -147,7 +147,7 @@ namespace Sarand.BusinessMgr.Web.Control.Controllers.Instance
// Construction.
public AjaxResponse_Repository(SbmModel_Control model) : base(model) { }
// Repository details.
public RepositoryItemData repository { get; } = new RepositoryItemData();
public RepositoryItemData repository { get; } = new();
}
//-------------------------------------------------------------------
[HttpPost]
@@ -367,6 +367,8 @@ namespace Sarand.BusinessMgr.Web.Control.Controllers.Instance
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + ControlInstanceMgr.ErrorStateMessage));
}
responseData.SetNotification(messageTitle, Localiser["msg.item.updated"]);
// In case Repository definition has changed since Instance was registered with Application.
App_Control.RefreshInstanceSessionProperties(repository);
}
// Construct response.
@@ -1,11 +1,9 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Consoles;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
using Sarand.BusinessMgr.Net.Standard.Base.Consoles;
using Sarand.BusinessMgr.Net.Standard.Base.Data;
using Sarand.BusinessMgr.Web.Control.Components;
using Sarand.BusinessMgr.Web.Control.Consoles;
@@ -13,7 +11,6 @@ using Sarand.BusinessMgr.Web.Control.Models.Instance;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.Security;
using Sarand.Net.Standard.Base.Utils;
using System;
@@ -198,6 +195,8 @@ namespace Sarand.BusinessMgr.Web.Control.Controllers.Instance
{
// Construction.
public AjaxResponse_RepositoryInitialise(SbmModel_Control model) : base(model) { }
// Repository details - to mimic response from GetRepositoryList.
public RepositoryController.RepositoryListData Repository { get; } = new();
}
[HttpPost]
[Route("Instance/Repository/Ajax/Initialise")]
@@ -228,6 +227,12 @@ namespace Sarand.BusinessMgr.Web.Control.Controllers.Instance
SaLogger.Warn($"{SysUtil.Fn} Repository does not exist. Id: {repositoryId}.");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
SbmcOwner owner = ControlInstanceMgr.GetOwnerById(repository.OwnerId);
if (owner == null)
{
SaLogger.Warn($"{SysUtil.Fn} Owner does not exist. Id: {repository.OwnerId}.");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
// Core Instance Console.
SbmCoreInstanceConsole_Control instanceConsole = new SbmCoreInstanceConsole_Control(SessionData_Control, repository);
@@ -397,6 +402,7 @@ namespace Sarand.BusinessMgr.Web.Control.Controllers.Instance
return Json(responseData.SetMessage(messageTitle, repositoryConsole.ErrorStateMessage));
// Complete response.
RepositoryController.PopulateRepositoryResponseListData(model, responseData.Repository, owner, repository);
responseData.Success = true;
}
return Json(responseData);
@@ -40,10 +40,13 @@
@* Filter Panel *@
<div class="sbc-panel-filter">
<div class="row pb4">
<div class="col-sm-5 sbc-label text-left text-sm-right">@Localiser["label.search"]</div>
<div class="col-sm-7">
@Html.TextBoxFor(model => model.Filter.Search, new { @type = "search", @id = "", @class = "FilterSearch", @maxlength = "32" })
<div class="row pb-0">
<div class="col-8"></div>
<div class="col-4 row">
<div class="col-sm-5 sbc-label text-left text-sm-right">@Localiser["label.search"]</div>
<div class="col-sm-7">
@Html.TextBoxFor(model => model.Filter.Search, new { @type = "search", @id = "", @class = "FilterSearch sbc-input", @maxlength = "32" })
</div>
</div>
</div>
</div>
@@ -40,10 +40,14 @@
@* Filter Panel *@
<div class="sbc-panel-filter">
<div class="row pb4">
<div class="col-sm-5 sbc-label text-left text-sm-right">@Localiser["label.search"]</div>
<div class="col-sm-7">
@Html.TextBoxFor(model => model.Filter.Search, new { @type = "search", @id = "", @class = "FilterSearch", @maxlength = "32" })
<div class="row pb-0">
<div class="col-sm-4"></div>
<div class="col-sm-4"></div>
<div class="col-sm-4 row">
<div class="col-sm-5 sbc-label text-left text-sm-right">@Localiser["label.search"]</div>
<div class="col-sm-7">
@Html.TextBoxFor(model => model.Filter.Search, new { @type = "search", @id = "", @class = "FilterSearch sbc-input", @maxlength = "32" })
</div>
</div>
</div>
</div>
@@ -41,16 +41,18 @@
<div class="sbc-panel-filter">
@Html.HiddenFor(model => model.DatabaseFilter.Instance, new { @id = "", @class = "FilterInstance" })
<div class="row">
<div class="col-sm-4 row pb4">
<div class="col-sm-4 row pb-0">
<div class="col-sm-5 sbc-label text-left text-sm-right">@Localiser["label.context"]</div>
<div class="col-sm-7">
@Html.DropDownListFor(model => model.DatabaseFilter.Context, Model.GetInstanceContextsForSelect(), "", new { @id = "", @class = "FilterContext" })
@Html.DropDownListFor(model => model.DatabaseFilter.Context, Model.GetInstanceContextsForSelect(), "", new { @id = "", @class = "FilterContext sbc-select" })
</div>
</div>
<div class="col-sm-4 row pb4">
<div class="col-sm-4 row pb-0">
</div>
<div class="col-sm-4 row pb-0">
<div class="col-sm-5 sbc-label text-left text-sm-right">@Localiser["label.search"]</div>
<div class="col-sm-7">
@Html.TextBoxFor(model => model.DatabaseFilter.Search, new { @type = "search", @id = "", @class = "FilterSearch", @maxlength = "32" })
@Html.TextBoxFor(model => model.DatabaseFilter.Search, new { @type = "search", @id = "", @class = "FilterSearch sbc-input", @maxlength = "32" })
</div>
</div>
</div>
@@ -38,16 +38,18 @@
@* Filter Panel *@
<div class="sbc-panel-filter">
<div class="row">
<div class="col-sm-4 row pb4">
<div class="col-sm-4 row pb-0">
<div class="col-sm-5 sbc-label text-left text-sm-right">@Localiser["label.context"]</div>
<div class="col-sm-7">
@Html.DropDownListFor(model => model.DatabaseFilter.Context, Model.GetInstanceContextsForSelect(), "", new { @id = "", @class = "FilterContext" })
@Html.DropDownListFor(model => model.DatabaseFilter.Context, Model.GetInstanceContextsForSelect(), "", new { @id = "", @class = "FilterContext sbc-select" })
</div>
</div>
<div class="col-sm-4 row pb4">
<div class="col-sm-4 row pb-0">
</div>
<div class="col-sm-4 row pb-0">
<div class="col-sm-5 sbc-label text-left text-sm-right">@Localiser["label.search"]</div>
<div class="col-sm-7">
@Html.TextBoxFor(model => model.DatabaseFilter.Search, new { @type = "search", @id = "", @class = "FilterSearch", @maxlength = "32" })
@Html.TextBoxFor(model => model.DatabaseFilter.Search, new { @type = "search", @id = "", @class = "FilterSearch sbc-input", @maxlength = "32" })
</div>
</div>
</div>
@@ -41,10 +41,14 @@
@* Filter Panel *@
<div class="sbc-panel-filter">
<div class="row pb4">
<div class="col-sm-5 sbc-label text-left text-sm-right">@Localiser["label.search"]</div>
<div class="col-sm-7">
@Html.TextBoxFor(model => model.Filter.Search, new { @type = "search", @id = "", @class = "FilterSearch", @maxlength = "32" })
<div class="row pb-0">
<div class="col-sm-4"></div>
<div class="col-sm-4"></div>
<div class="col-sm-4 row">
<div class="col-sm-5 sbc-label text-left text-sm-right">@Localiser["label.search"]</div>
<div class="col-sm-7">
@Html.TextBoxFor(model => model.Filter.Search, new { @type = "search", @id = "", @class = "FilterSearch sbc-input", @maxlength = "32" })
</div>
</div>
</div>
</div>
@@ -43,16 +43,16 @@
@* Filter Panel *@
<div class="sbc-panel-filter">
<div class="row">
<div class="col-sm-8 row pb4">
<div class="col-sm-8 row pb-0">
<div class="col-sm-3 sbc-label text-left text-sm-right">@Localiser["label.item"]</div>
<div class="col row">
<div class="fixed">
@Html.DropDownListFor(model => model.Filter.ItemType, Model.GetParamItemTypesForSelect(), new { @id = "", @class = "FilterItemType mr-1" })
<div class="fixed mb-1 mb-sm-0 mr-sm-1">
@Html.DropDownListFor(model => model.Filter.ItemType, Model.GetParamItemTypesForSelect(), new { @id = "", @class = "FilterItemType sbc-select" })
</div>
<div class="col-sm row">
<input type="hidden" id="" name="Filter.ItemId" class="FilterItemId" />
<div class="col">
<input type="text" id="" class="FilterItem fullwidth" readonly="readonly" style="" />
<div class="col mr-1">
<input type="text" id="" class="FilterItem sbc-input fullwidth" readonly="readonly" style="" />
</div>
<div class="fixed">
<div class="sbc-action FilterItemSearch"><img src="/img/action/search24.png" alt="" title=""></div>
@@ -63,7 +63,7 @@
<div class="col-sm-4 row pb4">
<div class="col-sm-4 sbc-label text-left text-sm-right">@Localiser["label.search"]</div>
<div class="col-sm-8">
@Html.TextBoxFor(model => model.Filter.Search, new { @type = "search", @id = "", @class = "FilterSearch fullwidth", @maxlength = "32" })
@Html.TextBoxFor(model => model.Filter.Search, new { @type = "search", @id = "", @class = "FilterSearch sbc-input fullwidth", @maxlength = "32" })
</div>
</div>
</div>
@@ -41,6 +41,7 @@
crossorigin="anonymous"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" />
</environment>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" />
<link rel="stylesheet" href="~/css/sbc-base.css?@Model.HrefVersion" />
<link rel="stylesheet" href="~/css/sbc-main.css?@Model.HrefVersion" />
<link rel="stylesheet" href="~/css/sbc-data.css?@Model.HrefVersion" />
@@ -6,6 +6,11 @@
}
<style>
.sbc-panel.LogFiles select
{
min-width: 100px;
}
#dlgLogFile .FileData
{
font-family: Courier New, Courier, monospace;
@@ -57,21 +62,29 @@
{
<input type="hidden" name="LogsFilter.Filename" class="FilterLogFilename" />
<div class="row">
<div class="col-sm-12 row mb-1">
<div class="col-sm-4 col-md-3 sbc-label text-left text-sm-right" style="padding-top:2px;">@Localiser["label.root.folder"]</div>
<div class="col-sm-8 col-md-9 sbc-value mt-1 bold">@(Model.LoggingRootFolder)</div>
<div class="col-sm-12 row mb-2">
<div class="col-sm-3 col-md-3 sbc-label pt-1 text-left text-sm-right">@Localiser["label.root.folder"]</div>
<div class="col-sm-9 col-md-9 sbc-value pt-1 bold">@(Model.LoggingRootFolder)</div>
</div>
<div class="col-sm-12 col-md-4 row">
<div class="col-sm-4 col-md-5 sbc-label text-left text-sm-right">@Localiser["label.folder"]</div>
<div class="col-sm-8 col-md-7 mb-1"><div style="max-width:200px;"><select id="cbFilterLogFolder" name="LogsFilter.Folder" class="FilterLogFolder"></select></div></div>
<div class="col-sm-3 col-md-4 sbc-label text-left text-sm-right">@Localiser["label.folder"]</div>
<div class="col-sm-9 col-md-8 mb-1"><div style="max-width:200px;"><select id="cbFilterLogFolder" name="LogsFilter.Folder" class="FilterLogFolder sbc-select"></select></div></div>
</div>
<div class="col-xs-12 col-md-4 row">
<div class="col-sm-3 col-md-4 sbc-label text-left text-sm-right">@Localiser["label.year"]</div>
<div class="col-sm-9 col-md-8 mb-1">
<select id="cbFilterLogYear" name="LogsFilter.Year" class="FilterLogYear sbc-select" style="max-width:100px;"></select>
<span class="sbc-icon fa-solid fa-chevron-left text-info icon-year-prev"></span>
<span class="sbc-icon fa-solid fa-chevron-right text-info icon-year-next"></span>
</div>
</div>
<div class="col-sm-12 col-md-4 row">
<div class="col-sm-4 col-md-5 sbc-label text-left text-sm-right">@Localiser["label.year"]</div>
<div class="col-sm-8 col-md-7 mb-1"><div style="max-width:200px;"><select id="cbFilterLogYear" name="LogsFilter.Year" class="FilterLogYear"></select></div></div>
</div>
<div class="col-sm-12 col-md-4 row">
<div class="col-sm-4 col-md-5 sbc-label text-left text-sm-right">@Localiser["label.month"]</div>
<div class="col-sm-8 col-md-7"><div style="max-width:200px;"><select id="cbFilterLogMonth" name="LogsFilter.Month" class="FilterLogMonth"></select></div></div>
<div class="col-sm-3 col-md-4 sbc-label text-left text-sm-right">@Localiser["label.month"]</div>
<div class="col-sm-9 col-md-8">
<select id="cbFilterLogMonth" name="LogsFilter.Month" class="FilterLogMonth sbc-select" style="max-width:100px;"></select>
<span class="sbc-icon fa-solid fa-chevron-left text-info icon-month-prev"></span>
<span class="sbc-icon fa-solid fa-chevron-right text-info icon-month-next"></span>
</div>
</div>
</div>
}
@@ -67,7 +67,7 @@
.sbc-panel-data .sbc-panel-filter .sbc-label
{
padding: 2px 8px 0 0;
padding: 4px 8px 0 0;
}
/* Data panel - Content */
@@ -167,8 +167,10 @@
.sbc-form .sbc-input, .sbc-form .sbc-textarea, .sbc-form .sbc-select
{
padding: 3px 8px 4px 8px;
box-sizing: border-box;
padding: 3px 8px 3px 8px;
width: 100%;
min-height: 1rem;
font-size: 1rem;
line-height: 1.25;
background-color: #fff;
@@ -232,6 +232,35 @@ a:focus
font-weight: bold;
}
/* Icons */
.sbc-icon
{
display: inline-block;
min-width: 24px;
height: 20px;
padding: 2px;
text-align: center;
background-size: contain;
background-repeat: no-repeat;
vertical-align: middle;
border: solid 1px #e0e0e0;
border-radius: 4px;
cursor: pointer;
}
.sbc-icon:active {
background-color: #d4af37;
box-shadow: inset 2px 2px 6px #aaa;
border-color: #b0b0b0;
transform: translateY(1px);
}
.sbc-icon:hover
{
border-color:#17a2b8;
}
/* Buttons */
.sbc-button-cta, .sbc-button-action
@@ -75,7 +75,7 @@
sbc.refreshControlValues(response.controlValues);
if (!response.success) return;
env.$dlg.init.dialog('close');
if (env.cfg.fnSuccess) env.cfg.fnSuccess();
if (env.cfg.fnSuccess) env.cfg.fnSuccess(response.repository);
delete env.cfg;
}
@@ -45,9 +45,11 @@
let items = response.repositories || [];
if (!items.length)
return $pnl.msgs.append($('<div class="sbc-message">' + sbc.msg.dataNoneFound + '</div>')).fadeIn(500);
for (let itemIdx = 0; itemIdx < items.length; itemIdx++)
$table.append(ir.populateItem(ir.getItemRowTemplate(), items[itemIdx]));
ir.refreshRepoInfo(items[0].id, 500);
for (const item of items)
{
$table.append(ir.populateItem(ir.getItemRowTemplate(), item));
ir.addRepoForInfo(item);
}
}
ir.populateItem = function ($item, item)
{
@@ -79,6 +81,29 @@
}
//--------------------------------------------------------------
const reposForInfo = [];
ir.addRepoForInfo = (item) =>
{
if (reposForInfo.some(repo => repo.id == item.id)) return;
ir.clearRepoInfo(item);
reposForInfo.push(item);
if (reposForInfo.length == 1) ir.refreshNextRepoInfo();
}
ir.clearRepoInfo = (item) =>
{
$pnl.table.find('#' + ir.getItemId(item)).find('.RepositoryInitialised').empty();
}
ir.resetRefreshRepoInfo = () =>
{
reposForInfo.length = 0;
sbc.cancelAjaxRequest(ir.ajax.repoInit);
}
ir.refreshNextRepoInfo = () =>
{
if (ir.ajax.repoInit?.timeout) return;
let repo = reposForInfo.shift();
if (repo?.id) ir.refreshRepoInfo(repo.id, 200);
}
ir.refreshRepoInfo = function (id, delay)
{
$frm.itemId.find('.RepositoryId').val(id);
@@ -103,13 +128,11 @@
}
else $repoInit.append($('<div>').append($('<img style="width:16px">').prop('src', '/img/icon/tick-24.png')));
$bgr.toggle(!!$('.sbc-dropdown-item', $mnu).length);
let $next = $item.next();
if ($next.length) ir.refreshRepoInfo($next.prop('id').replace(/[^0-9]/g, ''), 200);
ir.refreshNextRepoInfo();
}
ir.onInitRepo = function (id)
ir.onInitRepo = function (item)
{
//sbc.debug('onInitRepo', id);
ir.refreshResults(50);
ir.addRepoForInfo(item);
}
//--------------------------------------------------------------
@@ -237,6 +260,7 @@
$('table tbody', $pnl.table).prepend($item).show();
}
ir.populateItem($item, item);
ir.addRepoForInfo(item);
}
//--------------------------------------------------------------
@@ -136,6 +136,19 @@
var sel = $(':selected', $sel).length;
($info || $('.sbc-info', $sel.closest('.sbc-form-control'))).text(sel == 0 ? sbc.lbl.noneSelected : sbc.lbl.numberSelected.replace(/\{0\}/g, sel));
}
sbc.offsetSelectedOption = ($sel, offset) =>
{
var currentIndex = $sel.prop('selectedIndex');
var optionsCount = $sel.find('option').length;
if (optionsCount === 0) return;
var newIndex = currentIndex + offset;
if (newIndex < 0) newIndex = 0;
if (newIndex >= optionsCount) newIndex = optionsCount - 1;
if (newIndex != currentIndex)
$sel.prop('selectedIndex', newIndex).trigger('change');
}
sbc.initCalendars = function (sel, opt)
{
return $(sel).datepicker(opt || {});
@@ -148,6 +161,7 @@
$(this).datepicker('setDate', null).datepicker('hide').trigger('change');
});
}
sbc.refreshPaging = function (response, context, refreshFn)
{
if (sbc.resultsPaging.fn.refreshPaging)
@@ -341,15 +355,20 @@
//--------------------------------------------------------------
// Ajax.
sbc.doAjaxRequest = function (ajax, delay)
sbc.doAjaxRequest = (ajax, delay) =>
{
win.clearTimeout(ajax.timeout);
if (!ajax.noWaitIcon) sbc.doPleaseWait();
ajax.timeout = setTimeout(function () { sbc.execAjaxRequest(ajax); }, Math.max(50, parseInt(delay || 0, 10)));
}
sbc.execAjaxRequest = function (ajax)
sbc.cancelAjaxRequest = (ajax) =>
{
win.clearTimeout(ajax?.timeout);
}
sbc.execAjaxRequest = (ajax) =>
{
win.clearTimeout(ajax.timeout);
delete ajax.timeout;
if (ajax.request) ajax.request.abort();
var cfgAjax =
{
@@ -275,6 +275,10 @@
$ctrl.cbFolders = $frm.filter.find('#cbFilterLogFolder').on('change', function () { sl.refreshYears(250); });
$ctrl.cbYears = $frm.filter.find('#cbFilterLogYear').on('change', function () { sl.refreshMonths(250); });
$ctrl.cbMonths = $frm.filter.find('#cbFilterLogMonth').on('change', function () { sl.refreshFiles(250); });
$('.icon-year-prev').on('click', () => { sbc.offsetSelectedOption($ctrl.cbYears, -1) });
$('.icon-year-next').on('click', () => { sbc.offsetSelectedOption($ctrl.cbYears, 1) });
$('.icon-month-prev').on('click', () => { sbc.offsetSelectedOption($ctrl.cbMonths, -1) });
$('.icon-month-next').on('click', () => { sbc.offsetSelectedOption($ctrl.cbMonths, 1) });
var opt = sbc.dlgOpts();
opt.buttons[sbc.lbl.close] = function () { $(this).dialog('close'); sl.setFileData(); };