first commit
This commit is contained in:
390
Helper/ConfigHelper.cs
Normal file
390
Helper/ConfigHelper.cs
Normal file
@@ -0,0 +1,390 @@
|
||||
using MotoVaultPro.External.Interfaces;
|
||||
using MotoVaultPro.Models;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using System.Security.Claims;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace MotoVaultPro.Helper
|
||||
{
|
||||
public interface IConfigHelper
|
||||
{
|
||||
OpenIDConfig GetOpenIDConfig();
|
||||
ReminderUrgencyConfig GetReminderUrgencyConfig();
|
||||
MailConfig GetMailConfig();
|
||||
UserConfig GetUserConfig(ClaimsPrincipal user);
|
||||
bool SaveUserConfig(ClaimsPrincipal user, UserConfig configData);
|
||||
bool SaveServerConfig(ServerConfig serverConfig);
|
||||
bool AuthenticateRootUser(string username, string password);
|
||||
bool AuthenticateRootUserOIDC(string email);
|
||||
string GetWebHookUrl();
|
||||
bool GetCustomWidgetsEnabled();
|
||||
string GetMOTD();
|
||||
string GetLogoUrl();
|
||||
string GetSmallLogoUrl();
|
||||
string GetServerLanguage();
|
||||
bool GetServerDisabledRegistration();
|
||||
bool GetServerEnableShopSupplies();
|
||||
bool GetServerAuthEnabled();
|
||||
bool GetEnableRootUserOIDC();
|
||||
string GetServerPostgresConnection();
|
||||
string GetAllowedFileUploadExtensions();
|
||||
string GetServerDomain();
|
||||
bool DeleteUserConfig(int userId);
|
||||
bool GetInvariantApi();
|
||||
bool GetServerOpenRegistration();
|
||||
string GetDefaultReminderEmail();
|
||||
}
|
||||
public class ConfigHelper : IConfigHelper
|
||||
{
|
||||
private readonly IConfiguration _config;
|
||||
private readonly IUserConfigDataAccess _userConfig;
|
||||
private readonly ILogger<IConfigHelper> _logger;
|
||||
private IMemoryCache _cache;
|
||||
public ConfigHelper(IConfiguration serverConfig,
|
||||
IUserConfigDataAccess userConfig,
|
||||
IMemoryCache memoryCache,
|
||||
ILogger<IConfigHelper> logger)
|
||||
{
|
||||
_config = serverConfig;
|
||||
_userConfig = userConfig;
|
||||
_cache = memoryCache;
|
||||
_logger = logger;
|
||||
}
|
||||
public string GetWebHookUrl()
|
||||
{
|
||||
var webhook = CheckString("LUBELOGGER_WEBHOOK");
|
||||
return webhook;
|
||||
}
|
||||
public bool GetCustomWidgetsEnabled()
|
||||
{
|
||||
return CheckBool(CheckString("LUBELOGGER_CUSTOM_WIDGETS"));
|
||||
}
|
||||
public bool GetInvariantApi()
|
||||
{
|
||||
return CheckBool(CheckString("LUBELOGGER_INVARIANT_API"));
|
||||
}
|
||||
public string GetMOTD()
|
||||
{
|
||||
var motd = CheckString("LUBELOGGER_MOTD");
|
||||
return motd;
|
||||
}
|
||||
public string GetServerDomain()
|
||||
{
|
||||
var domain = CheckString("LUBELOGGER_DOMAIN");
|
||||
return domain;
|
||||
}
|
||||
public bool GetServerOpenRegistration()
|
||||
{
|
||||
return CheckBool(CheckString("LUBELOGGER_OPEN_REGISTRATION"));
|
||||
}
|
||||
public bool GetServerAuthEnabled()
|
||||
{
|
||||
return CheckBool(CheckString(nameof(UserConfig.EnableAuth)));
|
||||
}
|
||||
public OpenIDConfig GetOpenIDConfig()
|
||||
{
|
||||
OpenIDConfig openIdConfig = _config.GetSection("OpenIDConfig").Get<OpenIDConfig>() ?? new OpenIDConfig();
|
||||
return openIdConfig;
|
||||
}
|
||||
public ReminderUrgencyConfig GetReminderUrgencyConfig()
|
||||
{
|
||||
ReminderUrgencyConfig reminderUrgencyConfig = _config.GetSection("ReminderUrgencyConfig").Get<ReminderUrgencyConfig>() ?? new ReminderUrgencyConfig();
|
||||
return reminderUrgencyConfig;
|
||||
}
|
||||
public MailConfig GetMailConfig()
|
||||
{
|
||||
MailConfig mailConfig = _config.GetSection("MailConfig").Get<MailConfig>() ?? new MailConfig();
|
||||
return mailConfig;
|
||||
}
|
||||
public string GetLogoUrl()
|
||||
{
|
||||
var logoUrl = CheckString("LUBELOGGER_LOGO_URL", StaticHelper.DefaultLogoPath);
|
||||
return logoUrl;
|
||||
}
|
||||
public string GetSmallLogoUrl()
|
||||
{
|
||||
var logoUrl = CheckString("LUBELOGGER_LOGO_SMALL_URL", StaticHelper.DefaultSmallLogoPath);
|
||||
return logoUrl;
|
||||
}
|
||||
public string GetDefaultReminderEmail()
|
||||
{
|
||||
var reminderEmail = CheckString(nameof(ServerConfig.DefaultReminderEmail));
|
||||
return reminderEmail;
|
||||
}
|
||||
public string GetAllowedFileUploadExtensions()
|
||||
{
|
||||
var allowedFileExtensions = CheckString("LUBELOGGER_ALLOWED_FILE_EXTENSIONS", StaticHelper.DefaultAllowedFileExtensions);
|
||||
return allowedFileExtensions;
|
||||
}
|
||||
public bool AuthenticateRootUser(string username, string password)
|
||||
{
|
||||
var rootUsername = CheckString(nameof(UserConfig.UserNameHash));
|
||||
var rootPassword = CheckString(nameof(UserConfig.UserPasswordHash));
|
||||
if (string.IsNullOrWhiteSpace(rootUsername) || string.IsNullOrWhiteSpace(rootPassword))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return username == rootUsername && password == rootPassword;
|
||||
}
|
||||
public bool AuthenticateRootUserOIDC(string email)
|
||||
{
|
||||
var rootEmail = CheckString(nameof(ServerConfig.DefaultReminderEmail));
|
||||
var rootUserOIDC = CheckBool(CheckString(nameof(ServerConfig.EnableRootUserOIDC)));
|
||||
if (!rootUserOIDC || string.IsNullOrWhiteSpace(rootEmail))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return email == rootEmail;
|
||||
}
|
||||
public bool GetEnableRootUserOIDC()
|
||||
{
|
||||
var rootUserOIDC = CheckBool(CheckString(nameof(ServerConfig.EnableRootUserOIDC)));
|
||||
return rootUserOIDC;
|
||||
}
|
||||
public string GetServerLanguage()
|
||||
{
|
||||
var serverLanguage = CheckString(nameof(UserConfig.UserLanguage), "en_US");
|
||||
return serverLanguage;
|
||||
}
|
||||
public bool GetServerDisabledRegistration()
|
||||
{
|
||||
var registrationDisabled = CheckBool(CheckString(nameof(ServerConfig.DisableRegistration)));
|
||||
return registrationDisabled;
|
||||
}
|
||||
public string GetServerPostgresConnection()
|
||||
{
|
||||
var postgresConnection = CheckString("POSTGRES_CONNECTION");
|
||||
return postgresConnection;
|
||||
}
|
||||
public bool GetServerEnableShopSupplies()
|
||||
{
|
||||
return CheckBool(CheckString(nameof(UserConfig.EnableShopSupplies)));
|
||||
}
|
||||
public bool SaveServerConfig(ServerConfig serverConfig)
|
||||
{
|
||||
//nullify default values
|
||||
if (string.IsNullOrWhiteSpace(serverConfig.PostgresConnection))
|
||||
{
|
||||
serverConfig.PostgresConnection = null;
|
||||
}
|
||||
if (serverConfig.AllowedFileExtensions == StaticHelper.DefaultAllowedFileExtensions || string.IsNullOrWhiteSpace(serverConfig.AllowedFileExtensions))
|
||||
{
|
||||
serverConfig.AllowedFileExtensions = null;
|
||||
}
|
||||
if (serverConfig.CustomLogoURL == StaticHelper.DefaultLogoPath || string.IsNullOrWhiteSpace(serverConfig.CustomLogoURL))
|
||||
{
|
||||
serverConfig.CustomLogoURL = null;
|
||||
}
|
||||
if (serverConfig.CustomSmallLogoURL == StaticHelper.DefaultSmallLogoPath || string.IsNullOrWhiteSpace(serverConfig.CustomSmallLogoURL))
|
||||
{
|
||||
serverConfig.CustomSmallLogoURL = null;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(serverConfig.MessageOfTheDay))
|
||||
{
|
||||
serverConfig.MessageOfTheDay = null;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(serverConfig.WebHookURL))
|
||||
{
|
||||
serverConfig.WebHookURL = null;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(serverConfig.ServerURL))
|
||||
{
|
||||
serverConfig.ServerURL = null;
|
||||
}
|
||||
if (serverConfig.CustomWidgetsEnabled.HasValue && !serverConfig.CustomWidgetsEnabled.Value)
|
||||
{
|
||||
serverConfig.CustomWidgetsEnabled = null;
|
||||
}
|
||||
if (serverConfig.InvariantAPIEnabled.HasValue && !serverConfig.InvariantAPIEnabled.Value)
|
||||
{
|
||||
serverConfig.InvariantAPIEnabled = null;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(serverConfig.SMTPConfig?.EmailServer ?? string.Empty))
|
||||
{
|
||||
serverConfig.SMTPConfig = null;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(serverConfig.OIDCConfig?.Name ?? string.Empty))
|
||||
{
|
||||
serverConfig.OIDCConfig = null;
|
||||
}
|
||||
if (serverConfig.OpenRegistration.HasValue && !serverConfig.OpenRegistration.Value)
|
||||
{
|
||||
serverConfig.OpenRegistration = null;
|
||||
}
|
||||
if (serverConfig.DisableRegistration.HasValue && !serverConfig.DisableRegistration.Value)
|
||||
{
|
||||
serverConfig.DisableRegistration = null;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(serverConfig.DefaultReminderEmail))
|
||||
{
|
||||
serverConfig.DefaultReminderEmail = null;
|
||||
}
|
||||
if (serverConfig.EnableRootUserOIDC.HasValue && !serverConfig.EnableRootUserOIDC.Value)
|
||||
{
|
||||
serverConfig.EnableRootUserOIDC = null;
|
||||
}
|
||||
try
|
||||
{
|
||||
File.WriteAllText(StaticHelper.ServerConfigPath, JsonSerializer.Serialize(serverConfig));
|
||||
return true;
|
||||
} catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public bool SaveUserConfig(ClaimsPrincipal user, UserConfig configData)
|
||||
{
|
||||
var storedUserId = user.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
int userId = 0;
|
||||
if (storedUserId != null)
|
||||
{
|
||||
userId = int.Parse(storedUserId);
|
||||
}
|
||||
bool isRootUser = user.IsInRole(nameof(UserData.IsRootUser)) || userId == -1;
|
||||
if (isRootUser)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists(StaticHelper.UserConfigPath))
|
||||
{
|
||||
//if file doesn't exist it might be because it's running on a mounted volume in docker.
|
||||
File.WriteAllText(StaticHelper.UserConfigPath, JsonSerializer.Serialize(new UserConfig()));
|
||||
}
|
||||
var configFileContents = File.ReadAllText(StaticHelper.UserConfigPath);
|
||||
configData.EnableAuth = bool.Parse(_config[nameof(UserConfig.EnableAuth)] ?? "false");
|
||||
configData.UserNameHash = _config[nameof(UserConfig.UserNameHash)] ?? string.Empty;
|
||||
configData.UserPasswordHash = _config[nameof(UserConfig.UserPasswordHash)] ?? string.Empty;
|
||||
File.WriteAllText(StaticHelper.UserConfigPath, JsonSerializer.Serialize(configData));
|
||||
_cache.Set<UserConfig>($"userConfig_{userId}", configData);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex.Message);
|
||||
return false;
|
||||
}
|
||||
} else
|
||||
{
|
||||
var userConfig = new UserConfigData()
|
||||
{
|
||||
Id = userId,
|
||||
UserConfig = configData
|
||||
};
|
||||
var result = _userConfig.SaveUserConfig(userConfig);
|
||||
_cache.Set<UserConfig>($"userConfig_{userId}", configData);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
public bool DeleteUserConfig(int userId)
|
||||
{
|
||||
_cache.Remove($"userConfig_{userId}");
|
||||
var result = _userConfig.DeleteUserConfig(userId);
|
||||
return result;
|
||||
}
|
||||
private bool CheckBool(string value, bool defaultValue = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
else if (bool.TryParse(value, out bool result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
} catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning($"ConfigHelper Warning: You might be missing keys in appsettings.json, Message: ${ex.Message}");
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
private string CheckString(string configName, string defaultValue = "")
|
||||
{
|
||||
try
|
||||
{
|
||||
var configValue = _config[configName] ?? defaultValue;
|
||||
return configValue;
|
||||
} catch(Exception ex)
|
||||
{
|
||||
_logger.LogWarning($"ConfigHelper Warning: You might be missing keys in appsettings.json, Message: ${ex.Message}");
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
public UserConfig GetUserConfig(ClaimsPrincipal user)
|
||||
{
|
||||
var serverConfig = new UserConfig
|
||||
{
|
||||
EnableCsvImports = CheckBool(CheckString(nameof(UserConfig.EnableCsvImports)), true),
|
||||
UseDarkMode = CheckBool(CheckString(nameof(UserConfig.UseDarkMode))),
|
||||
UseSystemColorMode = CheckBool(CheckString(nameof(UserConfig.UseSystemColorMode)), true),
|
||||
UseMPG = CheckBool(CheckString(nameof(UserConfig.UseMPG)), true),
|
||||
UseDescending = CheckBool(CheckString(nameof(UserConfig.UseDescending))),
|
||||
EnableAuth = CheckBool(CheckString(nameof(UserConfig.EnableAuth))),
|
||||
HideZero = CheckBool(CheckString(nameof(UserConfig.HideZero))),
|
||||
AutomaticDecimalFormat = CheckBool(CheckString(nameof(UserConfig.AutomaticDecimalFormat))),
|
||||
UseUKMPG = CheckBool(CheckString(nameof(UserConfig.UseUKMPG))),
|
||||
UseMarkDownOnSavedNotes = CheckBool(CheckString(nameof(UserConfig.UseMarkDownOnSavedNotes))),
|
||||
UseThreeDecimalGasCost = CheckBool(CheckString(nameof(UserConfig.UseThreeDecimalGasCost)), true),
|
||||
UseThreeDecimalGasConsumption = CheckBool(CheckString(nameof(UserConfig.UseThreeDecimalGasConsumption)), true),
|
||||
EnableAutoReminderRefresh = CheckBool(CheckString(nameof(UserConfig.EnableAutoReminderRefresh))),
|
||||
EnableAutoOdometerInsert = CheckBool(CheckString(nameof(UserConfig.EnableAutoOdometerInsert))),
|
||||
PreferredGasMileageUnit = CheckString(nameof(UserConfig.PreferredGasMileageUnit)),
|
||||
PreferredGasUnit = CheckString(nameof(UserConfig.PreferredGasUnit)),
|
||||
UseUnitForFuelCost = CheckBool(CheckString(nameof(UserConfig.UseUnitForFuelCost))),
|
||||
UserLanguage = CheckString(nameof(UserConfig.UserLanguage), "en_US"),
|
||||
HideSoldVehicles = CheckBool(CheckString(nameof(UserConfig.HideSoldVehicles))),
|
||||
EnableShopSupplies = CheckBool(CheckString(nameof(UserConfig.EnableShopSupplies))),
|
||||
ShowCalendar = CheckBool(CheckString(nameof(UserConfig.ShowCalendar))),
|
||||
EnableExtraFieldColumns = CheckBool(CheckString(nameof(UserConfig.EnableExtraFieldColumns))),
|
||||
VisibleTabs = _config.GetSection(nameof(UserConfig.VisibleTabs)).Get<List<ImportMode>>() ?? new UserConfig().VisibleTabs,
|
||||
TabOrder = _config.GetSection(nameof(UserConfig.TabOrder)).Get<List<ImportMode>>() ?? new UserConfig().TabOrder,
|
||||
UserColumnPreferences = _config.GetSection(nameof(UserConfig.UserColumnPreferences)).Get<List<UserColumnPreference>>() ?? new List<UserColumnPreference>(),
|
||||
DefaultTab = (ImportMode)int.Parse(CheckString(nameof(UserConfig.DefaultTab), "8")),
|
||||
ShowVehicleThumbnail = CheckBool(CheckString(nameof(UserConfig.ShowVehicleThumbnail)))
|
||||
};
|
||||
int userId = 0;
|
||||
if (user != null)
|
||||
{
|
||||
var storedUserId = user.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
if (storedUserId != null)
|
||||
{
|
||||
userId = int.Parse(storedUserId);
|
||||
}
|
||||
} else
|
||||
{
|
||||
return serverConfig;
|
||||
}
|
||||
return _cache.GetOrCreate<UserConfig>($"userConfig_{userId}", entry =>
|
||||
{
|
||||
entry.SlidingExpiration = TimeSpan.FromHours(1);
|
||||
if (!user.Identity.IsAuthenticated)
|
||||
{
|
||||
return serverConfig;
|
||||
}
|
||||
bool isRootUser = user.IsInRole(nameof(UserData.IsRootUser)) || userId == -1;
|
||||
if (isRootUser)
|
||||
{
|
||||
return serverConfig;
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = _userConfig.GetUserConfig(userId);
|
||||
if (result == null)
|
||||
{
|
||||
return serverConfig;
|
||||
}
|
||||
else
|
||||
{
|
||||
return result.UserConfig;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
479
Helper/FileHelper.cs
Normal file
479
Helper/FileHelper.cs
Normal file
@@ -0,0 +1,479 @@
|
||||
using MotoVaultPro.Models;
|
||||
using System.IO.Compression;
|
||||
|
||||
namespace MotoVaultPro.Helper
|
||||
{
|
||||
public interface IFileHelper
|
||||
{
|
||||
string GetFullFilePath(string currentFilePath, bool mustExist = true);
|
||||
byte[] GetFileBytes(string fullFilePath, bool deleteFile = false);
|
||||
string MoveFileFromTemp(string currentFilePath, string newFolder);
|
||||
bool RenameFile(string currentFilePath, string newName);
|
||||
bool DeleteFile(string currentFilePath);
|
||||
string MakeBackup();
|
||||
bool RestoreBackup(string fileName, bool clearExisting = false);
|
||||
string MakeAttachmentsExport(List<GenericReportModel> exportData);
|
||||
List<string> GetLanguages();
|
||||
int ClearTempFolder();
|
||||
int ClearUnlinkedThumbnails(List<string> linkedImages);
|
||||
int ClearUnlinkedDocuments(List<string> linkedDocuments);
|
||||
string GetWidgets();
|
||||
bool WidgetsExist();
|
||||
bool SaveWidgets(string widgetsData);
|
||||
bool DeleteWidgets();
|
||||
}
|
||||
public class FileHelper : IFileHelper
|
||||
{
|
||||
private readonly IWebHostEnvironment _webEnv;
|
||||
private readonly ILogger<IFileHelper> _logger;
|
||||
private ILiteDBHelper _liteDB;
|
||||
public FileHelper(IWebHostEnvironment webEnv, ILogger<IFileHelper> logger, ILiteDBHelper liteDB)
|
||||
{
|
||||
_webEnv = webEnv;
|
||||
_logger = logger;
|
||||
_liteDB = liteDB;
|
||||
}
|
||||
public List<string> GetLanguages()
|
||||
{
|
||||
var languagePath = Path.Combine(_webEnv.ContentRootPath, "data", "translations");
|
||||
var defaultList = new List<string>() { "en_US" };
|
||||
if (Directory.Exists(languagePath))
|
||||
{
|
||||
var listOfLanguages = Directory.GetFiles(languagePath);
|
||||
if (listOfLanguages.Any())
|
||||
{
|
||||
defaultList.AddRange(listOfLanguages.Select(x => Path.GetFileNameWithoutExtension(x)));
|
||||
}
|
||||
}
|
||||
return defaultList;
|
||||
}
|
||||
public bool RenameFile(string currentFilePath, string newName)
|
||||
{
|
||||
var fullFilePath = GetFullFilePath(currentFilePath);
|
||||
if (!string.IsNullOrWhiteSpace(fullFilePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
var originalFileName = Path.GetFileNameWithoutExtension(fullFilePath);
|
||||
var newFilePath = fullFilePath.Replace(originalFileName, newName);
|
||||
File.Move(fullFilePath, newFilePath);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public string GetFullFilePath(string currentFilePath, bool mustExist = true)
|
||||
{
|
||||
if (currentFilePath.StartsWith("/"))
|
||||
{
|
||||
currentFilePath = currentFilePath.Substring(1);
|
||||
}
|
||||
string oldFilePath = currentFilePath.StartsWith("defaults/") ? Path.Combine(_webEnv.WebRootPath, currentFilePath) : Path.Combine(_webEnv.ContentRootPath, "data", currentFilePath);
|
||||
if (File.Exists(oldFilePath))
|
||||
{
|
||||
return oldFilePath;
|
||||
}
|
||||
else if (!mustExist)
|
||||
{
|
||||
return oldFilePath;
|
||||
}
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
public byte[] GetFileBytes(string fullFilePath, bool deleteFile = false)
|
||||
{
|
||||
if (File.Exists(fullFilePath))
|
||||
{
|
||||
var fileBytes = File.ReadAllBytes(fullFilePath);
|
||||
if (deleteFile)
|
||||
{
|
||||
File.Delete(fullFilePath);
|
||||
}
|
||||
return fileBytes;
|
||||
}
|
||||
return Array.Empty<byte>();
|
||||
}
|
||||
public bool RestoreBackup(string fileName, bool clearExisting = false)
|
||||
{
|
||||
var fullFilePath = GetFullFilePath(fileName);
|
||||
if (string.IsNullOrWhiteSpace(fullFilePath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
var tempPath = Path.Combine(_webEnv.ContentRootPath, "data", $"temp/{Guid.NewGuid()}");
|
||||
if (!Directory.Exists(tempPath))
|
||||
Directory.CreateDirectory(tempPath);
|
||||
//extract zip file
|
||||
ZipFile.ExtractToDirectory(fullFilePath, tempPath);
|
||||
//copy over images and documents.
|
||||
var imagePath = Path.Combine(tempPath, "images");
|
||||
var documentPath = Path.Combine(tempPath, "documents");
|
||||
var translationPath = Path.Combine(tempPath, "translations");
|
||||
var dataPath = Path.Combine(tempPath, StaticHelper.DbName);
|
||||
var widgetPath = Path.Combine(tempPath, StaticHelper.AdditionalWidgetsPath);
|
||||
var configPath = Path.Combine(tempPath, StaticHelper.LegacyUserConfigPath);
|
||||
var serverConfigPath = Path.Combine(tempPath, StaticHelper.LegacyServerConfigPath);
|
||||
if (Directory.Exists(imagePath))
|
||||
{
|
||||
var existingPath = Path.Combine(_webEnv.ContentRootPath, "data", "images");
|
||||
if (!Directory.Exists(existingPath))
|
||||
{
|
||||
Directory.CreateDirectory(existingPath);
|
||||
}
|
||||
else if (clearExisting)
|
||||
{
|
||||
var filesToDelete = Directory.GetFiles(existingPath);
|
||||
foreach (string file in filesToDelete)
|
||||
{
|
||||
File.Delete(file);
|
||||
}
|
||||
}
|
||||
//copy each files from temp folder to newPath
|
||||
var filesToUpload = Directory.GetFiles(imagePath);
|
||||
foreach (string file in filesToUpload)
|
||||
{
|
||||
File.Copy(file, $"{existingPath}/{Path.GetFileName(file)}", true);
|
||||
}
|
||||
}
|
||||
if (Directory.Exists(documentPath))
|
||||
{
|
||||
var existingPath = Path.Combine(_webEnv.ContentRootPath, "data", "documents");
|
||||
if (!Directory.Exists(existingPath))
|
||||
{
|
||||
Directory.CreateDirectory(existingPath);
|
||||
}
|
||||
else if (clearExisting)
|
||||
{
|
||||
var filesToDelete = Directory.GetFiles(existingPath);
|
||||
foreach (string file in filesToDelete)
|
||||
{
|
||||
File.Delete(file);
|
||||
}
|
||||
}
|
||||
//copy each files from temp folder to newPath
|
||||
var filesToUpload = Directory.GetFiles(documentPath);
|
||||
foreach (string file in filesToUpload)
|
||||
{
|
||||
File.Copy(file, $"{existingPath}/{Path.GetFileName(file)}", true);
|
||||
}
|
||||
}
|
||||
if (Directory.Exists(translationPath))
|
||||
{
|
||||
var existingPath = Path.Combine(_webEnv.ContentRootPath, "data", "translations");
|
||||
if (!Directory.Exists(existingPath))
|
||||
{
|
||||
Directory.CreateDirectory(existingPath);
|
||||
}
|
||||
else if (clearExisting)
|
||||
{
|
||||
var filesToDelete = Directory.GetFiles(existingPath);
|
||||
foreach (string file in filesToDelete)
|
||||
{
|
||||
File.Delete(file);
|
||||
}
|
||||
}
|
||||
//copy each files from temp folder to newPath
|
||||
var filesToUpload = Directory.GetFiles(translationPath);
|
||||
foreach (string file in filesToUpload)
|
||||
{
|
||||
File.Copy(file, $"{existingPath}/{Path.GetFileName(file)}", true);
|
||||
}
|
||||
}
|
||||
if (File.Exists(dataPath))
|
||||
{
|
||||
//Relinquish current DB file lock
|
||||
_liteDB.DisposeLiteDB();
|
||||
//data path will always exist as it is created on startup if not.
|
||||
File.Move(dataPath, StaticHelper.DbName, true);
|
||||
}
|
||||
if (File.Exists(widgetPath))
|
||||
{
|
||||
File.Move(widgetPath, StaticHelper.AdditionalWidgetsPath, true);
|
||||
}
|
||||
if (File.Exists(configPath))
|
||||
{
|
||||
//check if config folder exists.
|
||||
if (!Directory.Exists("data/config"))
|
||||
{
|
||||
Directory.CreateDirectory("data/config");
|
||||
}
|
||||
File.Move(configPath, StaticHelper.UserConfigPath, true);
|
||||
}
|
||||
if (File.Exists(serverConfigPath))
|
||||
{
|
||||
//check if config folder exists.
|
||||
if (!Directory.Exists("data/config"))
|
||||
{
|
||||
Directory.CreateDirectory("data/config");
|
||||
}
|
||||
File.Move(serverConfigPath, StaticHelper.ServerConfigPath, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"Error Restoring Database Backup: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public string MakeAttachmentsExport(List<GenericReportModel> exportData)
|
||||
{
|
||||
var folderName = Guid.NewGuid();
|
||||
var tempPath = Path.Combine(_webEnv.ContentRootPath, "data", $"temp/{folderName}");
|
||||
if (!Directory.Exists(tempPath))
|
||||
Directory.CreateDirectory(tempPath);
|
||||
int fileIndex = 0;
|
||||
foreach (GenericReportModel reportModel in exportData)
|
||||
{
|
||||
foreach (UploadedFiles file in reportModel.Files)
|
||||
{
|
||||
var fileToCopy = GetFullFilePath(file.Location);
|
||||
var destFileName = $"{tempPath}/{fileIndex}_{reportModel.DataType}_{reportModel.Date.ToString("yyyy-MM-dd")}_{file.Name}{Path.GetExtension(file.Location)}";
|
||||
File.Copy(fileToCopy, destFileName);
|
||||
fileIndex++;
|
||||
}
|
||||
}
|
||||
var destFilePath = $"{tempPath}.zip";
|
||||
ZipFile.CreateFromDirectory(tempPath, destFilePath);
|
||||
//delete temp directory
|
||||
Directory.Delete(tempPath, true);
|
||||
var zipFileName = $"/temp/{folderName}.zip";
|
||||
return zipFileName;
|
||||
}
|
||||
public string MakeBackup()
|
||||
{
|
||||
var folderName = $"db_backup_{DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss")}";
|
||||
var tempPath = Path.Combine(_webEnv.ContentRootPath, "data", $"temp/{folderName}");
|
||||
var imagePath = Path.Combine(_webEnv.ContentRootPath, "data", "images");
|
||||
var documentPath = Path.Combine(_webEnv.ContentRootPath, "data", "documents");
|
||||
var translationPath = Path.Combine(_webEnv.ContentRootPath, "data", "translations");
|
||||
var dataPath = StaticHelper.DbName;
|
||||
var widgetPath = StaticHelper.AdditionalWidgetsPath;
|
||||
var configPath = StaticHelper.UserConfigPath;
|
||||
var serverConfigPath = StaticHelper.ServerConfigPath;
|
||||
if (!Directory.Exists(tempPath))
|
||||
Directory.CreateDirectory(tempPath);
|
||||
if (Directory.Exists(imagePath))
|
||||
{
|
||||
var files = Directory.GetFiles(imagePath);
|
||||
foreach (var file in files)
|
||||
{
|
||||
var newPath = Path.Combine(tempPath, "images");
|
||||
Directory.CreateDirectory(newPath);
|
||||
File.Copy(file, $"{newPath}/{Path.GetFileName(file)}");
|
||||
}
|
||||
}
|
||||
if (Directory.Exists(documentPath))
|
||||
{
|
||||
var files = Directory.GetFiles(documentPath);
|
||||
foreach (var file in files)
|
||||
{
|
||||
var newPath = Path.Combine(tempPath, "documents");
|
||||
Directory.CreateDirectory(newPath);
|
||||
File.Copy(file, $"{newPath}/{Path.GetFileName(file)}");
|
||||
}
|
||||
}
|
||||
if (Directory.Exists(translationPath))
|
||||
{
|
||||
var files = Directory.GetFiles(translationPath);
|
||||
foreach(var file in files)
|
||||
{
|
||||
var newPath = Path.Combine(tempPath, "translations");
|
||||
Directory.CreateDirectory(newPath);
|
||||
File.Copy(file, $"{newPath}/{Path.GetFileName(file)}");
|
||||
}
|
||||
}
|
||||
if (File.Exists(dataPath))
|
||||
{
|
||||
var newPath = Path.Combine(tempPath, "data");
|
||||
Directory.CreateDirectory(newPath);
|
||||
File.Copy(dataPath, $"{newPath}/{Path.GetFileName(dataPath)}");
|
||||
}
|
||||
if (File.Exists(widgetPath))
|
||||
{
|
||||
var newPath = Path.Combine(tempPath, "data");
|
||||
Directory.CreateDirectory(newPath);
|
||||
File.Copy(widgetPath, $"{newPath}/{Path.GetFileName(widgetPath)}");
|
||||
}
|
||||
if (File.Exists(configPath))
|
||||
{
|
||||
var newPath = Path.Combine(tempPath, "config");
|
||||
Directory.CreateDirectory(newPath);
|
||||
File.Copy(configPath, $"{newPath}/{Path.GetFileName(configPath)}");
|
||||
}
|
||||
if (File.Exists(serverConfigPath))
|
||||
{
|
||||
var newPath = Path.Combine(tempPath, "config");
|
||||
Directory.CreateDirectory(newPath);
|
||||
File.Copy(serverConfigPath, $"{newPath}/{Path.GetFileName(serverConfigPath)}");
|
||||
}
|
||||
var destFilePath = $"{tempPath}.zip";
|
||||
ZipFile.CreateFromDirectory(tempPath, destFilePath);
|
||||
//delete temp directory
|
||||
Directory.Delete(tempPath, true);
|
||||
return $"/temp/{folderName}.zip";
|
||||
}
|
||||
public string MoveFileFromTemp(string currentFilePath, string newFolder)
|
||||
{
|
||||
string tempPath = "temp/";
|
||||
if (string.IsNullOrWhiteSpace(currentFilePath) || !currentFilePath.StartsWith("/temp/")) //file is not in temp directory.
|
||||
{
|
||||
return currentFilePath;
|
||||
}
|
||||
if (currentFilePath.StartsWith("/"))
|
||||
{
|
||||
currentFilePath = currentFilePath.Substring(1);
|
||||
}
|
||||
string uploadPath = Path.Combine(_webEnv.ContentRootPath, "data", newFolder);
|
||||
string oldFilePath = Path.Combine(_webEnv.ContentRootPath, "data", currentFilePath);
|
||||
if (!Directory.Exists(uploadPath))
|
||||
Directory.CreateDirectory(uploadPath);
|
||||
string newFileUploadPath = oldFilePath.Replace(tempPath, newFolder);
|
||||
if (File.Exists(oldFilePath))
|
||||
{
|
||||
File.Move(oldFilePath, newFileUploadPath);
|
||||
}
|
||||
string newFilePathToReturn = "/" + currentFilePath.Replace(tempPath, newFolder);
|
||||
return newFilePathToReturn;
|
||||
}
|
||||
public bool DeleteFile(string currentFilePath)
|
||||
{
|
||||
if (currentFilePath.StartsWith("/"))
|
||||
{
|
||||
currentFilePath = currentFilePath.Substring(1);
|
||||
}
|
||||
string filePath = Path.Combine(_webEnv.ContentRootPath, "data", currentFilePath);
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
File.Delete(filePath);
|
||||
}
|
||||
if (!File.Exists(filePath)) //verify file no longer exists.
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public int ClearTempFolder()
|
||||
{
|
||||
int filesDeleted = 0;
|
||||
var tempPath = GetFullFilePath("temp", false);
|
||||
if (Directory.Exists(tempPath))
|
||||
{
|
||||
//delete files
|
||||
var files = Directory.GetFiles(tempPath);
|
||||
foreach (var file in files)
|
||||
{
|
||||
File.Delete(file);
|
||||
filesDeleted++;
|
||||
}
|
||||
//delete folders
|
||||
var folders = Directory.GetDirectories(tempPath);
|
||||
foreach(var folder in folders)
|
||||
{
|
||||
Directory.Delete(folder, true);
|
||||
filesDeleted++;
|
||||
}
|
||||
}
|
||||
return filesDeleted;
|
||||
}
|
||||
public int ClearUnlinkedThumbnails(List<string> linkedImages)
|
||||
{
|
||||
int filesDeleted = 0;
|
||||
var imagePath = GetFullFilePath("images", false);
|
||||
if (Directory.Exists(imagePath))
|
||||
{
|
||||
var files = Directory.GetFiles(imagePath);
|
||||
foreach(var file in files)
|
||||
{
|
||||
if (!linkedImages.Contains(Path.GetFileName(file)))
|
||||
{
|
||||
File.Delete(file);
|
||||
filesDeleted++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return filesDeleted;
|
||||
}
|
||||
public int ClearUnlinkedDocuments(List<string> linkedDocuments)
|
||||
{
|
||||
int filesDeleted = 0;
|
||||
var documentPath = GetFullFilePath("documents", false);
|
||||
if (Directory.Exists(documentPath))
|
||||
{
|
||||
var files = Directory.GetFiles(documentPath);
|
||||
foreach (var file in files)
|
||||
{
|
||||
if (!linkedDocuments.Contains(Path.GetFileName(file)))
|
||||
{
|
||||
File.Delete(file);
|
||||
filesDeleted++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return filesDeleted;
|
||||
}
|
||||
public string GetWidgets()
|
||||
{
|
||||
if (File.Exists(StaticHelper.AdditionalWidgetsPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
//read file
|
||||
var widgets = File.ReadAllText(StaticHelper.AdditionalWidgetsPath);
|
||||
return widgets;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.Message);
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
public bool WidgetsExist()
|
||||
{
|
||||
return File.Exists(StaticHelper.AdditionalWidgetsPath);
|
||||
}
|
||||
public bool SaveWidgets(string widgetsData)
|
||||
{
|
||||
try
|
||||
{
|
||||
//Delete Widgets if exists
|
||||
DeleteWidgets();
|
||||
File.WriteAllText(StaticHelper.AdditionalWidgetsPath, widgetsData);
|
||||
return true;
|
||||
} catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public bool DeleteWidgets()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(StaticHelper.AdditionalWidgetsPath))
|
||||
{
|
||||
File.Delete(StaticHelper.AdditionalWidgetsPath);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
146
Helper/GasHelper.cs
Normal file
146
Helper/GasHelper.cs
Normal file
@@ -0,0 +1,146 @@
|
||||
using MotoVaultPro.Models;
|
||||
|
||||
namespace MotoVaultPro.Helper
|
||||
{
|
||||
public interface IGasHelper
|
||||
{
|
||||
List<GasRecordViewModel> GetGasRecordViewModels(List<GasRecord> result, bool useMPG, bool useUKMPG);
|
||||
string GetAverageGasMileage(List<GasRecordViewModel> results, bool useMPG);
|
||||
}
|
||||
public class GasHelper : IGasHelper
|
||||
{
|
||||
public string GetAverageGasMileage(List<GasRecordViewModel> results, bool useMPG)
|
||||
{
|
||||
var recordsToCalculate = results.Where(x => x.IncludeInAverage);
|
||||
if (recordsToCalculate.Any())
|
||||
{
|
||||
try
|
||||
{
|
||||
var totalMileage = recordsToCalculate.Sum(x => x.DeltaMileage);
|
||||
var totalGallons = recordsToCalculate.Sum(x => x.Gallons);
|
||||
var averageGasMileage = totalMileage / totalGallons;
|
||||
if (!useMPG && averageGasMileage > 0)
|
||||
{
|
||||
averageGasMileage = 100 / averageGasMileage;
|
||||
}
|
||||
return averageGasMileage.ToString("F");
|
||||
} catch (Exception ex)
|
||||
{
|
||||
return "0";
|
||||
}
|
||||
}
|
||||
return "0";
|
||||
}
|
||||
public List<GasRecordViewModel> GetGasRecordViewModels(List<GasRecord> result, bool useMPG, bool useUKMPG)
|
||||
{
|
||||
//need to order by to get correct results
|
||||
result = result.OrderBy(x => x.Date).ThenBy(x => x.Mileage).ToList();
|
||||
var computedResults = new List<GasRecordViewModel>();
|
||||
int previousMileage = 0;
|
||||
decimal unFactoredConsumption = 0.00M;
|
||||
int unFactoredMileage = 0;
|
||||
//perform computation.
|
||||
for (int i = 0; i < result.Count; i++)
|
||||
{
|
||||
var currentObject = result[i];
|
||||
decimal convertedConsumption;
|
||||
if (useUKMPG && useMPG)
|
||||
{
|
||||
//if we're using UK MPG and the user wants imperial calculation insteace of l/100km
|
||||
//if UK MPG is selected then the gas consumption are stored in liters but need to convert into UK gallons for computation.
|
||||
convertedConsumption = currentObject.Gallons / 4.546M;
|
||||
}
|
||||
else
|
||||
{
|
||||
convertedConsumption = currentObject.Gallons;
|
||||
}
|
||||
if (i > 0)
|
||||
{
|
||||
var deltaMileage = currentObject.Mileage - previousMileage;
|
||||
if (deltaMileage < 0)
|
||||
{
|
||||
deltaMileage = 0;
|
||||
}
|
||||
var gasRecordViewModel = new GasRecordViewModel()
|
||||
{
|
||||
Id = currentObject.Id,
|
||||
VehicleId = currentObject.VehicleId,
|
||||
MonthId = currentObject.Date.Month,
|
||||
Date = currentObject.Date.ToShortDateString(),
|
||||
Mileage = currentObject.Mileage,
|
||||
Gallons = convertedConsumption,
|
||||
Cost = currentObject.Cost,
|
||||
DeltaMileage = deltaMileage,
|
||||
CostPerGallon = convertedConsumption > 0.00M ? currentObject.Cost / convertedConsumption : 0,
|
||||
IsFillToFull = currentObject.IsFillToFull,
|
||||
MissedFuelUp = currentObject.MissedFuelUp,
|
||||
Notes = currentObject.Notes,
|
||||
Tags = currentObject.Tags,
|
||||
ExtraFields = currentObject.ExtraFields,
|
||||
Files = currentObject.Files
|
||||
};
|
||||
if (currentObject.MissedFuelUp)
|
||||
{
|
||||
//if they missed a fuel up, we skip MPG calculation.
|
||||
gasRecordViewModel.MilesPerGallon = 0;
|
||||
//reset unFactored vars for missed fuel up because the numbers wont be reliable.
|
||||
unFactoredConsumption = 0;
|
||||
unFactoredMileage = 0;
|
||||
}
|
||||
else if (currentObject.IsFillToFull && currentObject.Mileage != default)
|
||||
{
|
||||
//if user filled to full and an odometer is provided, otherwise we will defer calculations
|
||||
if (convertedConsumption > 0.00M && deltaMileage > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
gasRecordViewModel.MilesPerGallon = useMPG ? (unFactoredMileage + deltaMileage) / (unFactoredConsumption + convertedConsumption) : 100 / ((unFactoredMileage + deltaMileage) / (unFactoredConsumption + convertedConsumption));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
gasRecordViewModel.MilesPerGallon = 0;
|
||||
}
|
||||
}
|
||||
//reset unFactored vars
|
||||
unFactoredConsumption = 0;
|
||||
unFactoredMileage = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
unFactoredConsumption += convertedConsumption;
|
||||
unFactoredMileage += deltaMileage;
|
||||
gasRecordViewModel.MilesPerGallon = 0;
|
||||
}
|
||||
computedResults.Add(gasRecordViewModel);
|
||||
}
|
||||
else
|
||||
{
|
||||
computedResults.Add(new GasRecordViewModel()
|
||||
{
|
||||
Id = currentObject.Id,
|
||||
VehicleId = currentObject.VehicleId,
|
||||
MonthId = currentObject.Date.Month,
|
||||
Date = currentObject.Date.ToShortDateString(),
|
||||
Mileage = currentObject.Mileage,
|
||||
Gallons = convertedConsumption,
|
||||
Cost = currentObject.Cost,
|
||||
DeltaMileage = 0,
|
||||
MilesPerGallon = 0,
|
||||
CostPerGallon = convertedConsumption > 0.00M ? currentObject.Cost / convertedConsumption : 0,
|
||||
IsFillToFull = currentObject.IsFillToFull,
|
||||
MissedFuelUp = currentObject.MissedFuelUp,
|
||||
Notes = currentObject.Notes,
|
||||
Tags = currentObject.Tags,
|
||||
ExtraFields = currentObject.ExtraFields,
|
||||
Files = currentObject.Files
|
||||
});
|
||||
}
|
||||
if (currentObject.Mileage != default)
|
||||
{
|
||||
previousMileage = currentObject.Mileage;
|
||||
}
|
||||
}
|
||||
return computedResults;
|
||||
}
|
||||
}
|
||||
}
|
||||
36
Helper/LiteDBHelper.cs
Normal file
36
Helper/LiteDBHelper.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using LiteDB;
|
||||
|
||||
namespace MotoVaultPro.Helper;
|
||||
|
||||
public interface ILiteDBHelper
|
||||
{
|
||||
LiteDatabase GetLiteDB();
|
||||
void DisposeLiteDB();
|
||||
}
|
||||
public class LiteDBHelper: ILiteDBHelper
|
||||
{
|
||||
public LiteDatabase db { get; set; }
|
||||
public LiteDBHelper()
|
||||
{
|
||||
if (db == null)
|
||||
{
|
||||
db = new LiteDatabase(StaticHelper.DbName);
|
||||
}
|
||||
}
|
||||
public LiteDatabase GetLiteDB()
|
||||
{
|
||||
if (db == null)
|
||||
{
|
||||
db = new LiteDatabase(StaticHelper.DbName);
|
||||
}
|
||||
return db;
|
||||
}
|
||||
public void DisposeLiteDB()
|
||||
{
|
||||
if (db != null)
|
||||
{
|
||||
db.Dispose();
|
||||
db = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
259
Helper/MailHelper.cs
Normal file
259
Helper/MailHelper.cs
Normal file
@@ -0,0 +1,259 @@
|
||||
using MotoVaultPro.Models;
|
||||
using MimeKit;
|
||||
using MailKit.Net.Smtp;
|
||||
using MailKit.Security;
|
||||
|
||||
namespace MotoVaultPro.Helper
|
||||
{
|
||||
public interface IMailHelper
|
||||
{
|
||||
OperationResponse NotifyUserForRegistration(string emailAddress, string token);
|
||||
OperationResponse NotifyUserForPasswordReset(string emailAddress, string token);
|
||||
OperationResponse NotifyUserForAccountUpdate(string emailAddress, string token);
|
||||
OperationResponse NotifyUserForReminders(Vehicle vehicle, List<string> emailAddresses, List<ReminderRecordViewModel> reminders);
|
||||
OperationResponse SendTestEmail(string emailAddress, MailConfig testMailConfig);
|
||||
}
|
||||
public class MailHelper : IMailHelper
|
||||
{
|
||||
private readonly MailConfig mailConfig;
|
||||
private readonly string serverLanguage;
|
||||
private readonly string serverDomain;
|
||||
private readonly IFileHelper _fileHelper;
|
||||
private readonly ITranslationHelper _translator;
|
||||
private readonly ILogger<MailHelper> _logger;
|
||||
public MailHelper(
|
||||
IConfigHelper config,
|
||||
IFileHelper fileHelper,
|
||||
ITranslationHelper translationHelper,
|
||||
ILogger<MailHelper> logger
|
||||
) {
|
||||
//load mailConfig from Configuration
|
||||
mailConfig = config.GetMailConfig();
|
||||
serverLanguage = config.GetServerLanguage();
|
||||
serverDomain = config.GetServerDomain();
|
||||
_fileHelper = fileHelper;
|
||||
_translator = translationHelper;
|
||||
_logger = logger;
|
||||
}
|
||||
public OperationResponse NotifyUserForRegistration(string emailAddress, string token)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(mailConfig.EmailServer))
|
||||
{
|
||||
return OperationResponse.Failed("SMTP Server Not Setup");
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(emailAddress) || string.IsNullOrWhiteSpace(token)) {
|
||||
return OperationResponse.Failed("Email Address or Token is invalid");
|
||||
}
|
||||
string emailSubject = _translator.Translate(serverLanguage, "Your Registration Token for MotoVaultPro");
|
||||
string tokenHtml = token;
|
||||
if (!string.IsNullOrWhiteSpace(serverDomain))
|
||||
{
|
||||
string cleanedURL = serverDomain.EndsWith('/') ? serverDomain.TrimEnd('/') : serverDomain;
|
||||
//construct registration URL.
|
||||
tokenHtml = $"<a href='{cleanedURL}/Login/Registration?email={emailAddress}&token={token}' target='_blank'>{token}</a>";
|
||||
}
|
||||
string emailBody = $"<span>{_translator.Translate(serverLanguage, "A token has been generated on your behalf, please complete your registration for MotoVaultPro using the token")}: {tokenHtml}</span>";
|
||||
var result = SendEmail(new List<string> { emailAddress }, emailSubject, emailBody);
|
||||
if (result)
|
||||
{
|
||||
return OperationResponse.Succeed("Email Sent!");
|
||||
} else
|
||||
{
|
||||
return OperationResponse.Failed();
|
||||
}
|
||||
}
|
||||
public OperationResponse NotifyUserForPasswordReset(string emailAddress, string token)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(mailConfig.EmailServer))
|
||||
{
|
||||
return OperationResponse.Failed("SMTP Server Not Setup");
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(emailAddress) || string.IsNullOrWhiteSpace(token))
|
||||
{
|
||||
return OperationResponse.Failed("Email Address or Token is invalid");
|
||||
}
|
||||
string emailSubject = _translator.Translate(serverLanguage, "Your Password Reset Token for MotoVaultPro");
|
||||
string tokenHtml = token;
|
||||
if (!string.IsNullOrWhiteSpace(serverDomain))
|
||||
{
|
||||
string cleanedURL = serverDomain.EndsWith('/') ? serverDomain.TrimEnd('/') : serverDomain;
|
||||
//construct registration URL.
|
||||
tokenHtml = $"<a href='{cleanedURL}/Login/ResetPassword?email={emailAddress}&token={token}' target='_blank'>{token}</a>";
|
||||
}
|
||||
string emailBody = $"<span>{_translator.Translate(serverLanguage, "A token has been generated on your behalf, please reset your password for MotoVaultPro using the token")}: {tokenHtml}</span>";
|
||||
var result = SendEmail(new List<string> { emailAddress }, emailSubject, emailBody);
|
||||
if (result)
|
||||
{
|
||||
return OperationResponse.Succeed("Email Sent!");
|
||||
}
|
||||
else
|
||||
{
|
||||
return OperationResponse.Failed();
|
||||
}
|
||||
}
|
||||
public OperationResponse SendTestEmail(string emailAddress, MailConfig testMailConfig)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(testMailConfig.EmailServer))
|
||||
{
|
||||
return OperationResponse.Failed("SMTP Server Not Setup");
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(emailAddress))
|
||||
{
|
||||
return OperationResponse.Failed("Email Address or Token is invalid");
|
||||
}
|
||||
string emailSubject = _translator.Translate(serverLanguage, "Test Email from MotoVaultPro");
|
||||
string emailBody = _translator.Translate(serverLanguage, "If you are seeing this email it means your SMTP configuration is functioning correctly");
|
||||
var result = SendEmail(testMailConfig, new List<string> { emailAddress }, emailSubject, emailBody);
|
||||
if (result)
|
||||
{
|
||||
return OperationResponse.Succeed("Email Sent!");
|
||||
}
|
||||
else
|
||||
{
|
||||
return OperationResponse.Failed();
|
||||
}
|
||||
}
|
||||
public OperationResponse NotifyUserForAccountUpdate(string emailAddress, string token)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(mailConfig.EmailServer))
|
||||
{
|
||||
return OperationResponse.Failed("SMTP Server Not Setup");
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(emailAddress) || string.IsNullOrWhiteSpace(token))
|
||||
{
|
||||
return OperationResponse.Failed("Email Address or Token is invalid");
|
||||
}
|
||||
string emailSubject = _translator.Translate(serverLanguage, "Your User Account Update Token for MotoVaultPro");
|
||||
string emailBody = $"{_translator.Translate(serverLanguage, "A token has been generated on your behalf, please update your account for MotoVaultPro using the token")}: {token}";
|
||||
var result = SendEmail(new List<string> { emailAddress}, emailSubject, emailBody);
|
||||
if (result)
|
||||
{
|
||||
return OperationResponse.Succeed("Email Sent!");
|
||||
}
|
||||
else
|
||||
{
|
||||
return OperationResponse.Failed();
|
||||
}
|
||||
}
|
||||
public OperationResponse NotifyUserForReminders(Vehicle vehicle, List<string> emailAddresses, List<ReminderRecordViewModel> reminders)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(mailConfig.EmailServer))
|
||||
{
|
||||
return OperationResponse.Failed("SMTP Server Not Setup");
|
||||
}
|
||||
if (!emailAddresses.Any())
|
||||
{
|
||||
return OperationResponse.Failed("No recipients could be found");
|
||||
}
|
||||
if (!reminders.Any())
|
||||
{
|
||||
return OperationResponse.Failed("No reminders could be found");
|
||||
}
|
||||
//get email template, this file has to exist since it's a static file.
|
||||
var emailTemplatePath = _fileHelper.GetFullFilePath(StaticHelper.ReminderEmailTemplate);
|
||||
string emailSubject = $"{_translator.Translate(serverLanguage, "Vehicle Reminders From MotoVaultPro")} - {DateTime.Now.ToShortDateString()}";
|
||||
//construct html table.
|
||||
string emailBody = File.ReadAllText(emailTemplatePath);
|
||||
emailBody = emailBody.Replace("{VehicleInformation}", $"{vehicle.Year} {vehicle.Make} {vehicle.Model} #{StaticHelper.GetVehicleIdentifier(vehicle)}");
|
||||
string tableHeader = $"<th>{_translator.Translate(serverLanguage, "Urgency")}</th><th>{_translator.Translate(serverLanguage, "Description")}</th><th>{_translator.Translate(serverLanguage, "Due")}</th>";
|
||||
string tableBody = "";
|
||||
foreach(ReminderRecordViewModel reminder in reminders)
|
||||
{
|
||||
var dueOn = reminder.Metric == ReminderMetric.Both ? $"{reminder.Date.ToShortDateString()} or {reminder.Mileage}" : reminder.Metric == ReminderMetric.Date ? $"{reminder.Date.ToShortDateString()}" : $"{reminder.Mileage}";
|
||||
tableBody += $"<tr class='{reminder.Urgency}'><td>{_translator.Translate(serverLanguage, StaticHelper.GetTitleCaseReminderUrgency(reminder.Urgency))}</td><td>{reminder.Description}</td><td>{dueOn}</td></tr>";
|
||||
}
|
||||
emailBody = emailBody.Replace("{TableHeader}", tableHeader).Replace("{TableBody}", tableBody);
|
||||
try
|
||||
{
|
||||
var result = SendEmail(emailAddresses, emailSubject, emailBody);
|
||||
if (result)
|
||||
{
|
||||
return OperationResponse.Succeed("Email Sent!");
|
||||
} else
|
||||
{
|
||||
return OperationResponse.Failed();
|
||||
}
|
||||
} catch (Exception ex)
|
||||
{
|
||||
return OperationResponse.Failed(ex.Message);
|
||||
}
|
||||
}
|
||||
private bool SendEmail(List<string> emailTo, string emailSubject, string emailBody) {
|
||||
string from = mailConfig.EmailFrom;
|
||||
var server = mailConfig.EmailServer;
|
||||
var message = new MimeMessage();
|
||||
message.From.Add(new MailboxAddress(from, from));
|
||||
foreach(string emailRecipient in emailTo)
|
||||
{
|
||||
message.To.Add(new MailboxAddress(emailRecipient, emailRecipient));
|
||||
}
|
||||
message.Subject = emailSubject;
|
||||
|
||||
var builder = new BodyBuilder();
|
||||
|
||||
builder.HtmlBody = emailBody;
|
||||
|
||||
message.Body = builder.ToMessageBody();
|
||||
|
||||
using (var client = new SmtpClient())
|
||||
{
|
||||
client.Connect(server, mailConfig.Port, SecureSocketOptions.Auto);
|
||||
//perform authentication if either username or password is provided.
|
||||
//do not perform authentication if neither are provided.
|
||||
if (!string.IsNullOrWhiteSpace(mailConfig.Username) || !string.IsNullOrWhiteSpace(mailConfig.Password)) {
|
||||
client.Authenticate(mailConfig.Username, mailConfig.Password);
|
||||
}
|
||||
try
|
||||
{
|
||||
client.Send(message);
|
||||
client.Disconnect(true);
|
||||
return true;
|
||||
} catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
private bool SendEmail(MailConfig testMailConfig, List<string> emailTo, string emailSubject, string emailBody)
|
||||
{
|
||||
string from = testMailConfig.EmailFrom;
|
||||
var server = testMailConfig.EmailServer;
|
||||
var message = new MimeMessage();
|
||||
message.From.Add(new MailboxAddress(from, from));
|
||||
foreach (string emailRecipient in emailTo)
|
||||
{
|
||||
message.To.Add(new MailboxAddress(emailRecipient, emailRecipient));
|
||||
}
|
||||
message.Subject = emailSubject;
|
||||
|
||||
var builder = new BodyBuilder();
|
||||
|
||||
builder.HtmlBody = emailBody;
|
||||
|
||||
message.Body = builder.ToMessageBody();
|
||||
|
||||
using (var client = new SmtpClient())
|
||||
{
|
||||
client.Connect(server, testMailConfig.Port, SecureSocketOptions.Auto);
|
||||
//perform authentication if either username or password is provided.
|
||||
//do not perform authentication if neither are provided.
|
||||
if (!string.IsNullOrWhiteSpace(testMailConfig.Username) || !string.IsNullOrWhiteSpace(testMailConfig.Password))
|
||||
{
|
||||
client.Authenticate(testMailConfig.Username, testMailConfig.Password);
|
||||
}
|
||||
try
|
||||
{
|
||||
client.Send(message);
|
||||
client.Disconnect(true);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
175
Helper/ReminderHelper.cs
Normal file
175
Helper/ReminderHelper.cs
Normal file
@@ -0,0 +1,175 @@
|
||||
using MotoVaultPro.Models;
|
||||
|
||||
namespace MotoVaultPro.Helper
|
||||
{
|
||||
public interface IReminderHelper
|
||||
{
|
||||
ReminderRecord GetUpdatedRecurringReminderRecord(ReminderRecord existingReminder, DateTime? currentDate, int? currentMileage);
|
||||
List<ReminderRecordViewModel> GetReminderRecordViewModels(List<ReminderRecord> reminders, int currentMileage, DateTime dateCompare);
|
||||
}
|
||||
public class ReminderHelper: IReminderHelper
|
||||
{
|
||||
private readonly IConfigHelper _config;
|
||||
public ReminderHelper(IConfigHelper config)
|
||||
{
|
||||
_config = config;
|
||||
}
|
||||
public ReminderRecord GetUpdatedRecurringReminderRecord(ReminderRecord existingReminder, DateTime? currentDate, int? currentMileage)
|
||||
{
|
||||
var newDate = currentDate ?? existingReminder.Date;
|
||||
var newMileage = currentMileage ?? existingReminder.Mileage;
|
||||
if (existingReminder.Metric == ReminderMetric.Both)
|
||||
{
|
||||
if (existingReminder.ReminderMonthInterval != ReminderMonthInterval.Other)
|
||||
{
|
||||
existingReminder.Date = newDate.AddMonths((int)existingReminder.ReminderMonthInterval);
|
||||
} else
|
||||
{
|
||||
if (existingReminder.CustomMonthIntervalUnit == ReminderIntervalUnit.Months)
|
||||
{
|
||||
existingReminder.Date = newDate.Date.AddMonths(existingReminder.CustomMonthInterval);
|
||||
}
|
||||
else if (existingReminder.CustomMonthIntervalUnit == ReminderIntervalUnit.Days)
|
||||
{
|
||||
existingReminder.Date = newDate.Date.AddDays(existingReminder.CustomMonthInterval);
|
||||
}
|
||||
}
|
||||
|
||||
if (existingReminder.ReminderMileageInterval != ReminderMileageInterval.Other)
|
||||
{
|
||||
existingReminder.Mileage = newMileage + (int)existingReminder.ReminderMileageInterval;
|
||||
}
|
||||
else
|
||||
{
|
||||
existingReminder.Mileage = newMileage + existingReminder.CustomMileageInterval;
|
||||
}
|
||||
}
|
||||
else if (existingReminder.Metric == ReminderMetric.Odometer)
|
||||
{
|
||||
if (existingReminder.ReminderMileageInterval != ReminderMileageInterval.Other)
|
||||
{
|
||||
existingReminder.Mileage = newMileage + (int)existingReminder.ReminderMileageInterval;
|
||||
} else
|
||||
{
|
||||
existingReminder.Mileage = newMileage + existingReminder.CustomMileageInterval;
|
||||
}
|
||||
}
|
||||
else if (existingReminder.Metric == ReminderMetric.Date)
|
||||
{
|
||||
if (existingReminder.ReminderMonthInterval != ReminderMonthInterval.Other)
|
||||
{
|
||||
existingReminder.Date = newDate.AddMonths((int)existingReminder.ReminderMonthInterval);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (existingReminder.CustomMonthIntervalUnit == ReminderIntervalUnit.Months)
|
||||
{
|
||||
existingReminder.Date = newDate.AddMonths(existingReminder.CustomMonthInterval);
|
||||
}
|
||||
else if (existingReminder.CustomMonthIntervalUnit == ReminderIntervalUnit.Days)
|
||||
{
|
||||
existingReminder.Date = newDate.AddDays(existingReminder.CustomMonthInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
return existingReminder;
|
||||
}
|
||||
public List<ReminderRecordViewModel> GetReminderRecordViewModels(List<ReminderRecord> reminders, int currentMileage, DateTime dateCompare)
|
||||
{
|
||||
List<ReminderRecordViewModel> reminderViewModels = new List<ReminderRecordViewModel>();
|
||||
var reminderUrgencyConfig = _config.GetReminderUrgencyConfig();
|
||||
foreach (var reminder in reminders)
|
||||
{
|
||||
if (reminder.UseCustomThresholds)
|
||||
{
|
||||
reminderUrgencyConfig = reminder.CustomThresholds;
|
||||
}
|
||||
var reminderViewModel = new ReminderRecordViewModel()
|
||||
{
|
||||
Id = reminder.Id,
|
||||
VehicleId = reminder.VehicleId,
|
||||
Date = reminder.Date,
|
||||
Mileage = reminder.Mileage,
|
||||
Description = reminder.Description,
|
||||
Notes = reminder.Notes,
|
||||
Metric = reminder.Metric,
|
||||
UserMetric = reminder.Metric,
|
||||
IsRecurring = reminder.IsRecurring,
|
||||
Tags = reminder.Tags
|
||||
};
|
||||
if (reminder.Metric == ReminderMetric.Both)
|
||||
{
|
||||
if (reminder.Date < dateCompare)
|
||||
{
|
||||
reminderViewModel.Urgency = ReminderUrgency.PastDue;
|
||||
reminderViewModel.Metric = ReminderMetric.Date;
|
||||
}
|
||||
else if (reminder.Mileage < currentMileage)
|
||||
{
|
||||
reminderViewModel.Urgency = ReminderUrgency.PastDue;
|
||||
reminderViewModel.Metric = ReminderMetric.Odometer;
|
||||
}
|
||||
else if (reminder.Date < dateCompare.AddDays(reminderUrgencyConfig.VeryUrgentDays))
|
||||
{
|
||||
//if less than a week from today or less than 50 miles from current mileage then very urgent.
|
||||
reminderViewModel.Urgency = ReminderUrgency.VeryUrgent;
|
||||
//have to specify by which metric this reminder is urgent.
|
||||
reminderViewModel.Metric = ReminderMetric.Date;
|
||||
}
|
||||
else if (reminder.Mileage < currentMileage + reminderUrgencyConfig.VeryUrgentDistance)
|
||||
{
|
||||
reminderViewModel.Urgency = ReminderUrgency.VeryUrgent;
|
||||
reminderViewModel.Metric = ReminderMetric.Odometer;
|
||||
}
|
||||
else if (reminder.Date < dateCompare.AddDays(reminderUrgencyConfig.UrgentDays))
|
||||
{
|
||||
reminderViewModel.Urgency = ReminderUrgency.Urgent;
|
||||
reminderViewModel.Metric = ReminderMetric.Date;
|
||||
}
|
||||
else if (reminder.Mileage < currentMileage + reminderUrgencyConfig.UrgentDistance)
|
||||
{
|
||||
reminderViewModel.Urgency = ReminderUrgency.Urgent;
|
||||
reminderViewModel.Metric = ReminderMetric.Odometer;
|
||||
}
|
||||
reminderViewModel.DueDays = (reminder.Date - dateCompare).Days;
|
||||
reminderViewModel.DueMileage = reminder.Mileage - currentMileage;
|
||||
}
|
||||
else if (reminder.Metric == ReminderMetric.Date)
|
||||
{
|
||||
if (reminder.Date < dateCompare)
|
||||
{
|
||||
reminderViewModel.Urgency = ReminderUrgency.PastDue;
|
||||
}
|
||||
else if (reminder.Date < dateCompare.AddDays(reminderUrgencyConfig.VeryUrgentDays))
|
||||
{
|
||||
reminderViewModel.Urgency = ReminderUrgency.VeryUrgent;
|
||||
}
|
||||
else if (reminder.Date < dateCompare.AddDays(reminderUrgencyConfig.UrgentDays))
|
||||
{
|
||||
reminderViewModel.Urgency = ReminderUrgency.Urgent;
|
||||
}
|
||||
reminderViewModel.DueDays = (reminder.Date - dateCompare).Days;
|
||||
}
|
||||
else if (reminder.Metric == ReminderMetric.Odometer)
|
||||
{
|
||||
if (reminder.Mileage < currentMileage)
|
||||
{
|
||||
reminderViewModel.Urgency = ReminderUrgency.PastDue;
|
||||
reminderViewModel.Metric = ReminderMetric.Odometer;
|
||||
}
|
||||
else if (reminder.Mileage < currentMileage + reminderUrgencyConfig.VeryUrgentDistance)
|
||||
{
|
||||
reminderViewModel.Urgency = ReminderUrgency.VeryUrgent;
|
||||
}
|
||||
else if (reminder.Mileage < currentMileage + reminderUrgencyConfig.UrgentDistance)
|
||||
{
|
||||
reminderViewModel.Urgency = ReminderUrgency.Urgent;
|
||||
}
|
||||
reminderViewModel.DueMileage = reminder.Mileage - currentMileage;
|
||||
}
|
||||
reminderViewModels.Add(reminderViewModel);
|
||||
}
|
||||
return reminderViewModels;
|
||||
}
|
||||
}
|
||||
}
|
||||
170
Helper/ReportHelper.cs
Normal file
170
Helper/ReportHelper.cs
Normal file
@@ -0,0 +1,170 @@
|
||||
using MotoVaultPro.Models;
|
||||
using System.Globalization;
|
||||
|
||||
namespace MotoVaultPro.Helper
|
||||
{
|
||||
public interface IReportHelper
|
||||
{
|
||||
IEnumerable<CostForVehicleByMonth> GetOdometerRecordSum(List<OdometerRecord> odometerRecords, int year = 0, bool sortIntoYear = false);
|
||||
IEnumerable<CostForVehicleByMonth> GetServiceRecordSum(List<ServiceRecord> serviceRecords, int year = 0, bool sortIntoYear = false);
|
||||
IEnumerable<CostForVehicleByMonth> GetRepairRecordSum(List<CollisionRecord> repairRecords, int year = 0, bool sortIntoYear = false);
|
||||
IEnumerable<CostForVehicleByMonth> GetUpgradeRecordSum(List<UpgradeRecord> upgradeRecords, int year = 0, bool sortIntoYear = false);
|
||||
IEnumerable<CostForVehicleByMonth> GetGasRecordSum(List<GasRecord> gasRecords, int year = 0, bool sortIntoYear = false);
|
||||
IEnumerable<CostForVehicleByMonth> GetTaxRecordSum(List<TaxRecord> taxRecords, int year = 0, bool sortIntoYear = false);
|
||||
}
|
||||
public class ReportHelper: IReportHelper
|
||||
{
|
||||
public IEnumerable<CostForVehicleByMonth> GetOdometerRecordSum(List<OdometerRecord> odometerRecords, int year = 0, bool sortIntoYear = false)
|
||||
{
|
||||
if (year != default)
|
||||
{
|
||||
odometerRecords.RemoveAll(x => x.Date.Year != year);
|
||||
}
|
||||
if (sortIntoYear)
|
||||
{
|
||||
return odometerRecords.GroupBy(x => new { x.Date.Month, x.Date.Year }).OrderBy(x => x.Key.Month).Select(x => new CostForVehicleByMonth
|
||||
{
|
||||
MonthId = x.Key.Month,
|
||||
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key.Month),
|
||||
Year = x.Key.Year,
|
||||
Cost = 0,
|
||||
DistanceTraveled = x.Sum(y => y.DistanceTraveled)
|
||||
});
|
||||
} else
|
||||
{
|
||||
return odometerRecords.GroupBy(x => x.Date.Month).OrderBy(x => x.Key).Select(x => new CostForVehicleByMonth
|
||||
{
|
||||
MonthId = x.Key,
|
||||
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key),
|
||||
Cost = 0,
|
||||
DistanceTraveled = x.Sum(y => y.DistanceTraveled)
|
||||
});
|
||||
}
|
||||
}
|
||||
public IEnumerable<CostForVehicleByMonth> GetServiceRecordSum(List<ServiceRecord> serviceRecords, int year = 0, bool sortIntoYear = false)
|
||||
{
|
||||
if (year != default)
|
||||
{
|
||||
serviceRecords.RemoveAll(x => x.Date.Year != year);
|
||||
}
|
||||
if (sortIntoYear)
|
||||
{
|
||||
return serviceRecords.GroupBy(x => new { x.Date.Month, x.Date.Year }).OrderBy(x => x.Key.Month).Select(x => new CostForVehicleByMonth
|
||||
{
|
||||
MonthId = x.Key.Month,
|
||||
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key.Month),
|
||||
Year = x.Key.Year,
|
||||
Cost = x.Sum(y => y.Cost)
|
||||
});
|
||||
} else
|
||||
{
|
||||
return serviceRecords.GroupBy(x => x.Date.Month).OrderBy(x => x.Key).Select(x => new CostForVehicleByMonth
|
||||
{
|
||||
MonthId = x.Key,
|
||||
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key),
|
||||
Cost = x.Sum(y => y.Cost)
|
||||
});
|
||||
}
|
||||
}
|
||||
public IEnumerable<CostForVehicleByMonth> GetRepairRecordSum(List<CollisionRecord> repairRecords, int year = 0, bool sortIntoYear = false)
|
||||
{
|
||||
if (year != default)
|
||||
{
|
||||
repairRecords.RemoveAll(x => x.Date.Year != year);
|
||||
}
|
||||
if (sortIntoYear)
|
||||
{
|
||||
return repairRecords.GroupBy(x => new { x.Date.Month, x.Date.Year }).OrderBy(x => x.Key.Month).Select(x => new CostForVehicleByMonth
|
||||
{
|
||||
MonthId = x.Key.Month,
|
||||
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key.Month),
|
||||
Year = x.Key.Year,
|
||||
Cost = x.Sum(y => y.Cost)
|
||||
});
|
||||
} else
|
||||
{
|
||||
return repairRecords.GroupBy(x => x.Date.Month).OrderBy(x => x.Key).Select(x => new CostForVehicleByMonth
|
||||
{
|
||||
MonthId = x.Key,
|
||||
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key),
|
||||
Cost = x.Sum(y => y.Cost)
|
||||
});
|
||||
}
|
||||
}
|
||||
public IEnumerable<CostForVehicleByMonth> GetUpgradeRecordSum(List<UpgradeRecord> upgradeRecords, int year = 0, bool sortIntoYear = false)
|
||||
{
|
||||
if (year != default)
|
||||
{
|
||||
upgradeRecords.RemoveAll(x => x.Date.Year != year);
|
||||
}
|
||||
if (sortIntoYear)
|
||||
{
|
||||
return upgradeRecords.GroupBy(x => new { x.Date.Month, x.Date.Year }).OrderBy(x => x.Key.Month).Select(x => new CostForVehicleByMonth
|
||||
{
|
||||
MonthId = x.Key.Month,
|
||||
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key.Month),
|
||||
Year = x.Key.Year,
|
||||
Cost = x.Sum(y => y.Cost)
|
||||
});
|
||||
} else
|
||||
{
|
||||
return upgradeRecords.GroupBy(x => x.Date.Month).OrderBy(x => x.Key).Select(x => new CostForVehicleByMonth
|
||||
{
|
||||
MonthId = x.Key,
|
||||
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key),
|
||||
Cost = x.Sum(y => y.Cost)
|
||||
});
|
||||
}
|
||||
}
|
||||
public IEnumerable<CostForVehicleByMonth> GetGasRecordSum(List<GasRecord> gasRecords, int year = 0, bool sortIntoYear = false)
|
||||
{
|
||||
if (year != default)
|
||||
{
|
||||
gasRecords.RemoveAll(x => x.Date.Year != year);
|
||||
}
|
||||
if (sortIntoYear)
|
||||
{
|
||||
return gasRecords.GroupBy(x => new { x.Date.Month, x.Date.Year }).OrderBy(x => x.Key.Month).Select(x => new CostForVehicleByMonth
|
||||
{
|
||||
MonthId = x.Key.Month,
|
||||
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key.Month),
|
||||
Year = x.Key.Year,
|
||||
Cost = x.Sum(y => y.Cost)
|
||||
});
|
||||
} else
|
||||
{
|
||||
return gasRecords.GroupBy(x => x.Date.Month).OrderBy(x => x.Key).Select(x => new CostForVehicleByMonth
|
||||
{
|
||||
MonthId = x.Key,
|
||||
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key),
|
||||
Cost = x.Sum(y => y.Cost)
|
||||
});
|
||||
}
|
||||
}
|
||||
public IEnumerable<CostForVehicleByMonth> GetTaxRecordSum(List<TaxRecord> taxRecords, int year = 0, bool sortIntoYear = false)
|
||||
{
|
||||
if (year != default)
|
||||
{
|
||||
taxRecords.RemoveAll(x => x.Date.Year != year);
|
||||
}
|
||||
if (sortIntoYear)
|
||||
{
|
||||
return taxRecords.GroupBy(x => new { x.Date.Month, x.Date.Year }).OrderBy(x => x.Key.Month).Select(x => new CostForVehicleByMonth
|
||||
{
|
||||
MonthId = x.Key.Month,
|
||||
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key.Month),
|
||||
Year = x.Key.Year,
|
||||
Cost = x.Sum(y => y.Cost)
|
||||
});
|
||||
} else
|
||||
{
|
||||
return taxRecords.GroupBy(x => x.Date.Month).OrderBy(x => x.Key).Select(x => new CostForVehicleByMonth
|
||||
{
|
||||
MonthId = x.Key,
|
||||
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key),
|
||||
Cost = x.Sum(y => y.Cost)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
873
Helper/StaticHelper.cs
Normal file
873
Helper/StaticHelper.cs
Normal file
@@ -0,0 +1,873 @@
|
||||
using MotoVaultPro.Models;
|
||||
using CsvHelper;
|
||||
using System.Globalization;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace MotoVaultPro.Helper
|
||||
{
|
||||
/// <summary>
|
||||
/// helper method for static vars
|
||||
/// </summary>
|
||||
public static class StaticHelper
|
||||
{
|
||||
public const string VersionNumber = "1.4.9";
|
||||
public const string DbName = "data/cartracker.db";
|
||||
public const string UserConfigPath = "data/config/userConfig.json";
|
||||
public const string ServerConfigPath = "data/config/serverConfig.json";
|
||||
public const string LegacyUserConfigPath = "config/userConfig.json";
|
||||
public const string LegacyServerConfigPath = "config/serverConfig.json";
|
||||
public const string AdditionalWidgetsPath = "data/widgets.html";
|
||||
public const string DefaultLogoPath = "/defaults/motovaultpro_logo.png";
|
||||
public const string DefaultSmallLogoPath = "/defaults/motovaultpro_logo_small.png";
|
||||
public const string GenericErrorMessage = "An error occurred, please try again later";
|
||||
public const string ReminderEmailTemplate = "defaults/reminderemailtemplate.txt";
|
||||
public const string DefaultAllowedFileExtensions = ".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx";
|
||||
public const string SponsorsPath = "https://fbtech.github.io/fbtech/sponsors.json";
|
||||
public const string TranslationPath = "https://fbtech.github.io/motovaultpro_translations";
|
||||
public const string ReleasePath = "https://api.github.com/repos/fbtech/motovaultpro/releases/latest";
|
||||
public const string TranslationDirectoryPath = $"{TranslationPath}/directory.json";
|
||||
public const string ReportNote = "Report generated by MotoVaultPro, a Free and Open Source Vehicle Maintenance Tracker - MotoVaultPro.com";
|
||||
public static string GetTitleCaseReminderUrgency(ReminderUrgency input)
|
||||
{
|
||||
switch (input)
|
||||
{
|
||||
case ReminderUrgency.NotUrgent:
|
||||
return "Not Urgent";
|
||||
case ReminderUrgency.VeryUrgent:
|
||||
return "Very Urgent";
|
||||
case ReminderUrgency.PastDue:
|
||||
return "Past Due";
|
||||
default:
|
||||
return input.ToString();
|
||||
}
|
||||
}
|
||||
public static string GetTitleCaseReminderUrgency(string input)
|
||||
{
|
||||
switch (input)
|
||||
{
|
||||
case "NotUrgent":
|
||||
return "Not Urgent";
|
||||
case "VeryUrgent":
|
||||
return "Very Urgent";
|
||||
case "PastDue":
|
||||
return "Past Due";
|
||||
default:
|
||||
return input;
|
||||
}
|
||||
}
|
||||
public static string GetReminderUrgencyColor(ReminderUrgency input)
|
||||
{
|
||||
switch (input)
|
||||
{
|
||||
case ReminderUrgency.NotUrgent:
|
||||
return "text-bg-success";
|
||||
case ReminderUrgency.VeryUrgent:
|
||||
return "text-bg-danger";
|
||||
case ReminderUrgency.PastDue:
|
||||
return "text-bg-secondary";
|
||||
default:
|
||||
return "text-bg-warning";
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetPlanRecordColor(PlanPriority input)
|
||||
{
|
||||
switch (input)
|
||||
{
|
||||
case PlanPriority.Critical:
|
||||
return "text-bg-danger";
|
||||
case PlanPriority.Normal:
|
||||
return "text-bg-primary";
|
||||
case PlanPriority.Low:
|
||||
return "text-bg-info";
|
||||
default:
|
||||
return "text-bg-primary";
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetPlanRecordProgress(PlanProgress input)
|
||||
{
|
||||
switch (input)
|
||||
{
|
||||
case PlanProgress.Backlog:
|
||||
return "Planned";
|
||||
case PlanProgress.InProgress:
|
||||
return "Doing";
|
||||
case PlanProgress.Testing:
|
||||
return "Testing";
|
||||
case PlanProgress.Done:
|
||||
return "Done";
|
||||
default:
|
||||
return input.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public static string TruncateStrings(string input, int maxLength = 25)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
if (input.Length > maxLength)
|
||||
{
|
||||
return (input.Substring(0, maxLength) + "...");
|
||||
}
|
||||
else
|
||||
{
|
||||
return input;
|
||||
}
|
||||
}
|
||||
public static string DefaultActiveTab(UserConfig userConfig, ImportMode tab)
|
||||
{
|
||||
var defaultTab = userConfig.DefaultTab;
|
||||
var visibleTabs = userConfig.VisibleTabs;
|
||||
if (visibleTabs.Contains(tab) && tab == defaultTab)
|
||||
{
|
||||
return "active";
|
||||
}
|
||||
else if (!visibleTabs.Contains(tab))
|
||||
{
|
||||
return "d-none";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
public static string DefaultActiveTabContent(UserConfig userConfig, ImportMode tab)
|
||||
{
|
||||
var defaultTab = userConfig.DefaultTab;
|
||||
if (tab == defaultTab)
|
||||
{
|
||||
return "show active";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
public static string DefaultTabSelected(UserConfig userConfig, ImportMode tab)
|
||||
{
|
||||
var defaultTab = userConfig.DefaultTab;
|
||||
var visibleTabs = userConfig.VisibleTabs;
|
||||
if (!visibleTabs.Contains(tab))
|
||||
{
|
||||
return "disabled";
|
||||
}
|
||||
else if (tab == defaultTab)
|
||||
{
|
||||
return "selected";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
public static List<CostForVehicleByMonth> GetBaseLineCosts()
|
||||
{
|
||||
return new List<CostForVehicleByMonth>()
|
||||
{
|
||||
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(1), MonthId = 1, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(2), MonthId = 2, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(3), MonthId = 3, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(4), MonthId = 4, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(5), MonthId = 5, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(6), MonthId = 6, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(7), MonthId = 7, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(8), MonthId = 8, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(9), MonthId = 9, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(10), MonthId = 10, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(11), MonthId = 11, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(12), MonthId = 12, Cost = 0M}
|
||||
};
|
||||
}
|
||||
public static List<CostForVehicleByMonth> GetBaseLineCostsNoMonthName()
|
||||
{
|
||||
return new List<CostForVehicleByMonth>()
|
||||
{
|
||||
new CostForVehicleByMonth { MonthId = 1, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthId = 2, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthId = 3, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthId = 4, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthId = 5, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthId = 6, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthId = 7, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthId = 8, Cost = 0M},
|
||||
new CostForVehicleByMonth {MonthId = 9, Cost = 0M},
|
||||
new CostForVehicleByMonth { MonthId = 10, Cost = 0M},
|
||||
new CostForVehicleByMonth { MonthId = 11, Cost = 0M},
|
||||
new CostForVehicleByMonth { MonthId = 12, Cost = 0M}
|
||||
};
|
||||
}
|
||||
public static List<string> GetBarChartColors()
|
||||
{
|
||||
return new List<string> { "#00876c", "#43956e", "#67a371", "#89b177", "#a9be80", "#c8cb8b", "#e6d79b", "#e4c281", "#e3ab6b", "#e2925b", "#e07952", "#db5d4f" };
|
||||
}
|
||||
|
||||
public static ServiceRecord GenericToServiceRecord(GenericRecord input)
|
||||
{
|
||||
return new ServiceRecord
|
||||
{
|
||||
VehicleId = input.VehicleId,
|
||||
Date = input.Date,
|
||||
Description = input.Description,
|
||||
Cost = input.Cost,
|
||||
Mileage = input.Mileage,
|
||||
Files = input.Files,
|
||||
Notes = input.Notes,
|
||||
Tags = input.Tags,
|
||||
ExtraFields = input.ExtraFields,
|
||||
RequisitionHistory = input.RequisitionHistory
|
||||
};
|
||||
}
|
||||
public static CollisionRecord GenericToRepairRecord(GenericRecord input)
|
||||
{
|
||||
return new CollisionRecord
|
||||
{
|
||||
VehicleId = input.VehicleId,
|
||||
Date = input.Date,
|
||||
Description = input.Description,
|
||||
Cost = input.Cost,
|
||||
Mileage = input.Mileage,
|
||||
Files = input.Files,
|
||||
Notes = input.Notes,
|
||||
Tags = input.Tags,
|
||||
ExtraFields = input.ExtraFields,
|
||||
RequisitionHistory = input.RequisitionHistory
|
||||
};
|
||||
}
|
||||
public static UpgradeRecord GenericToUpgradeRecord(GenericRecord input)
|
||||
{
|
||||
return new UpgradeRecord
|
||||
{
|
||||
VehicleId = input.VehicleId,
|
||||
Date = input.Date,
|
||||
Description = input.Description,
|
||||
Cost = input.Cost,
|
||||
Mileage = input.Mileage,
|
||||
Files = input.Files,
|
||||
Notes = input.Notes,
|
||||
Tags = input.Tags,
|
||||
ExtraFields = input.ExtraFields,
|
||||
RequisitionHistory = input.RequisitionHistory
|
||||
};
|
||||
}
|
||||
|
||||
public static List<ExtraField> AddExtraFields(List<ExtraField> recordExtraFields, List<ExtraField> templateExtraFields)
|
||||
{
|
||||
if (!templateExtraFields.Any())
|
||||
{
|
||||
return new List<ExtraField>();
|
||||
}
|
||||
if (!recordExtraFields.Any())
|
||||
{
|
||||
return templateExtraFields;
|
||||
}
|
||||
var fieldNames = templateExtraFields.Select(x => x.Name);
|
||||
//remove fields that are no longer present in template.
|
||||
recordExtraFields.RemoveAll(x => !fieldNames.Contains(x.Name));
|
||||
if (!recordExtraFields.Any())
|
||||
{
|
||||
return templateExtraFields;
|
||||
}
|
||||
var recordFieldNames = recordExtraFields.Select(x => x.Name);
|
||||
//update isrequired setting
|
||||
foreach (ExtraField extraField in recordExtraFields)
|
||||
{
|
||||
var firstMatchingField = templateExtraFields.First(x => x.Name == extraField.Name);
|
||||
extraField.IsRequired = firstMatchingField.IsRequired;
|
||||
extraField.FieldType = firstMatchingField.FieldType;
|
||||
}
|
||||
//append extra fields
|
||||
foreach (ExtraField extraField in templateExtraFields)
|
||||
{
|
||||
if (!recordFieldNames.Contains(extraField.Name))
|
||||
{
|
||||
recordExtraFields.Add(extraField);
|
||||
}
|
||||
}
|
||||
//re-order extra fields
|
||||
recordExtraFields = recordExtraFields.OrderBy(x => templateExtraFields.FindIndex(y => y.Name == x.Name)).ToList();
|
||||
return recordExtraFields;
|
||||
}
|
||||
|
||||
public static string GetFuelEconomyUnit(bool useKwh, bool useHours, bool useMPG, bool useUKMPG)
|
||||
{
|
||||
string fuelEconomyUnit;
|
||||
if (useKwh)
|
||||
{
|
||||
var distanceUnit = useHours ? "h" : (useMPG ? "mi." : "km");
|
||||
fuelEconomyUnit = useMPG ? $"{distanceUnit}/kWh" : $"kWh/100{distanceUnit}";
|
||||
}
|
||||
else if (useMPG && useUKMPG)
|
||||
{
|
||||
fuelEconomyUnit = useHours ? "h/g" : "mpg";
|
||||
}
|
||||
else if (useUKMPG)
|
||||
{
|
||||
fuelEconomyUnit = useHours ? "l/100h" : "l/100mi.";
|
||||
}
|
||||
else
|
||||
{
|
||||
fuelEconomyUnit = useHours ? (useMPG ? "h/g" : "l/100h") : (useMPG ? "mpg" : "l/100km");
|
||||
}
|
||||
return fuelEconomyUnit;
|
||||
}
|
||||
public static long GetEpochFromDateTime(DateTime date)
|
||||
{
|
||||
return new DateTimeOffset(date).ToUnixTimeMilliseconds();
|
||||
}
|
||||
public static void InitMessage(IConfiguration config)
|
||||
{
|
||||
Console.WriteLine($"MotoVaultPro {VersionNumber}");
|
||||
Console.WriteLine("Website: https://motovaultpro.com");
|
||||
Console.WriteLine("Documentation: https://docs.motovaultpro.com");
|
||||
Console.WriteLine("GitHub: https://github.com/fbtech/motovaultpro");
|
||||
var mailConfig = config.GetSection("MailConfig").Get<MailConfig>();
|
||||
if (mailConfig != null && !string.IsNullOrWhiteSpace(mailConfig.EmailServer))
|
||||
{
|
||||
Console.WriteLine($"SMTP Configured for {mailConfig.EmailServer}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("SMTP Not Configured");
|
||||
}
|
||||
var motd = config["LUBELOGGER_MOTD"] ?? "Not Configured";
|
||||
Console.WriteLine($"Message Of The Day: {motd}");
|
||||
if (string.IsNullOrWhiteSpace(CultureInfo.CurrentCulture.Name))
|
||||
{
|
||||
Console.WriteLine("WARNING: No Locale or Culture Configured for MotoVaultPro, Check Environment Variables");
|
||||
}
|
||||
//Create folders if they don't exist.
|
||||
if (!Directory.Exists("data"))
|
||||
{
|
||||
Directory.CreateDirectory("data");
|
||||
Console.WriteLine("Created data directory");
|
||||
}
|
||||
if (!Directory.Exists("data/images"))
|
||||
{
|
||||
Console.WriteLine("Created images directory");
|
||||
Directory.CreateDirectory("data/images");
|
||||
}
|
||||
if (!Directory.Exists("data/documents"))
|
||||
{
|
||||
Directory.CreateDirectory("data/documents");
|
||||
Console.WriteLine("Created documents directory");
|
||||
}
|
||||
if (!Directory.Exists("data/translations"))
|
||||
{
|
||||
Directory.CreateDirectory("data/translations");
|
||||
Console.WriteLine("Created translations directory");
|
||||
}
|
||||
if (!Directory.Exists("data/temp"))
|
||||
{
|
||||
Directory.CreateDirectory("data/temp");
|
||||
Console.WriteLine("Created translations directory");
|
||||
}
|
||||
if (!Directory.Exists("data/config"))
|
||||
{
|
||||
Directory.CreateDirectory("data/config");
|
||||
Console.WriteLine("Created config directory");
|
||||
}
|
||||
}
|
||||
public static void CheckMigration(string webRootPath, string webContentPath)
|
||||
{
|
||||
//check if current working directory differs from content root.
|
||||
if (Directory.GetCurrentDirectory() != webContentPath)
|
||||
{
|
||||
Console.WriteLine("WARNING: The Working Directory differs from the Web Content Path");
|
||||
Console.WriteLine($"Working Directory: {Directory.GetCurrentDirectory()}");
|
||||
Console.WriteLine($"Web Content Path: {webContentPath}");
|
||||
}
|
||||
//migrates all user-uploaded files from webroot to new data folder
|
||||
//images
|
||||
var imagePath = Path.Combine(webRootPath, "images");
|
||||
var docsPath = Path.Combine(webRootPath, "documents");
|
||||
var translationPath = Path.Combine(webRootPath, "translations");
|
||||
var tempPath = Path.Combine(webRootPath, "temp");
|
||||
if (File.Exists(LegacyUserConfigPath))
|
||||
{
|
||||
File.Move(LegacyUserConfigPath, UserConfigPath, true);
|
||||
}
|
||||
if (Directory.Exists(imagePath))
|
||||
{
|
||||
foreach (string fileToMove in Directory.GetFiles(imagePath))
|
||||
{
|
||||
var newFilePath = $"data/images/{Path.GetFileName(fileToMove)}";
|
||||
File.Move(fileToMove, newFilePath, true);
|
||||
Console.WriteLine($"Migrated Image: {Path.GetFileName(fileToMove)}");
|
||||
}
|
||||
}
|
||||
if (Directory.Exists(docsPath))
|
||||
{
|
||||
foreach (string fileToMove in Directory.GetFiles(docsPath))
|
||||
{
|
||||
var newFilePath = $"data/documents/{Path.GetFileName(fileToMove)}";
|
||||
File.Move(fileToMove, newFilePath, true);
|
||||
Console.WriteLine($"Migrated Document: {Path.GetFileName(fileToMove)}");
|
||||
}
|
||||
}
|
||||
if (Directory.Exists(translationPath))
|
||||
{
|
||||
foreach (string fileToMove in Directory.GetFiles(translationPath))
|
||||
{
|
||||
var newFilePath = $"data/translations/{Path.GetFileName(fileToMove)}";
|
||||
File.Move(fileToMove, newFilePath, true);
|
||||
Console.WriteLine($"Migrated Translation: {Path.GetFileName(fileToMove)}");
|
||||
}
|
||||
}
|
||||
if (Directory.Exists(tempPath))
|
||||
{
|
||||
foreach (string fileToMove in Directory.GetFiles(tempPath))
|
||||
{
|
||||
var newFilePath = $"data/temp/{Path.GetFileName(fileToMove)}";
|
||||
File.Move(fileToMove, newFilePath, true);
|
||||
Console.WriteLine($"Migrated Temp File: {Path.GetFileName(fileToMove)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
public static async void NotifyAsync(string webhookURL, WebHookPayload webHookPayload)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(webhookURL))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var httpClient = new HttpClient();
|
||||
if (webhookURL.StartsWith("discord://"))
|
||||
{
|
||||
webhookURL = webhookURL.Replace("discord://", "https://"); //cleanurl
|
||||
//format to discord
|
||||
httpClient.PostAsJsonAsync(webhookURL, DiscordWebHook.FromWebHookPayload(webHookPayload));
|
||||
}
|
||||
else
|
||||
{
|
||||
httpClient.PostAsJsonAsync(webhookURL, webHookPayload);
|
||||
}
|
||||
}
|
||||
public static string GetImportModeIcon(ImportMode importMode)
|
||||
{
|
||||
switch (importMode)
|
||||
{
|
||||
case ImportMode.ServiceRecord:
|
||||
return "bi-card-checklist";
|
||||
case ImportMode.RepairRecord:
|
||||
return "bi-exclamation-octagon";
|
||||
case ImportMode.UpgradeRecord:
|
||||
return "bi-wrench-adjustable";
|
||||
case ImportMode.TaxRecord:
|
||||
return "bi-currency-dollar";
|
||||
case ImportMode.SupplyRecord:
|
||||
return "bi-shop";
|
||||
case ImportMode.PlanRecord:
|
||||
return "bi-bar-chart-steps";
|
||||
case ImportMode.OdometerRecord:
|
||||
return "bi-speedometer";
|
||||
case ImportMode.GasRecord:
|
||||
return "bi-fuel-pump";
|
||||
case ImportMode.NoteRecord:
|
||||
return "bi-journal-bookmark";
|
||||
case ImportMode.ReminderRecord:
|
||||
return "bi-bell";
|
||||
default:
|
||||
return "bi-file-bar-graph";
|
||||
}
|
||||
}
|
||||
public static string GetVehicleIdentifier(Vehicle vehicle)
|
||||
{
|
||||
if (vehicle.VehicleIdentifier == "LicensePlate")
|
||||
{
|
||||
return vehicle.LicensePlate;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (vehicle.ExtraFields.Any(x => x.Name == vehicle.VehicleIdentifier))
|
||||
{
|
||||
return vehicle.ExtraFields?.FirstOrDefault(x => x.Name == vehicle.VehicleIdentifier)?.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "N/A";
|
||||
}
|
||||
}
|
||||
}
|
||||
public static string GetVehicleIdentifier(VehicleViewModel vehicle)
|
||||
{
|
||||
if (vehicle.VehicleIdentifier == "LicensePlate")
|
||||
{
|
||||
return vehicle.LicensePlate;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (vehicle.ExtraFields.Any(x => x.Name == vehicle.VehicleIdentifier))
|
||||
{
|
||||
return vehicle.ExtraFields?.FirstOrDefault(x => x.Name == vehicle.VehicleIdentifier)?.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "N/A";
|
||||
}
|
||||
}
|
||||
}
|
||||
//Translations
|
||||
public static string GetTranslationDownloadPath(string continent, string name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(continent) || string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (continent)
|
||||
{
|
||||
case "NorthAmerica":
|
||||
continent = "North America";
|
||||
break;
|
||||
case "SouthAmerica":
|
||||
continent = "South America";
|
||||
break;
|
||||
}
|
||||
return $"{TranslationPath}/{continent}/{name}.json";
|
||||
}
|
||||
}
|
||||
public static string GetTranslationName(string name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
string cleanedName = name.Contains("_") ? name.Replace("_", "-") : name;
|
||||
string displayName = CultureInfo.GetCultureInfo(cleanedName).DisplayName;
|
||||
if (string.IsNullOrWhiteSpace(displayName))
|
||||
{
|
||||
return name;
|
||||
}
|
||||
else
|
||||
{
|
||||
return displayName;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
//CSV Write Methods
|
||||
public static void WriteGenericRecordExportModel(CsvWriter _csv, IEnumerable<GenericRecordExportModel> genericRecords)
|
||||
{
|
||||
var extraHeaders = genericRecords.SelectMany(x => x.ExtraFields).Select(y => y.Name).Distinct();
|
||||
//write headers
|
||||
_csv.WriteField(nameof(GenericRecordExportModel.Date));
|
||||
_csv.WriteField(nameof(GenericRecordExportModel.Description));
|
||||
_csv.WriteField(nameof(GenericRecordExportModel.Cost));
|
||||
_csv.WriteField(nameof(GenericRecordExportModel.Notes));
|
||||
_csv.WriteField(nameof(GenericRecordExportModel.Odometer));
|
||||
_csv.WriteField(nameof(GenericRecordExportModel.Tags));
|
||||
foreach (string extraHeader in extraHeaders)
|
||||
{
|
||||
_csv.WriteField($"extrafield_{extraHeader}");
|
||||
}
|
||||
_csv.NextRecord();
|
||||
foreach (GenericRecordExportModel genericRecord in genericRecords)
|
||||
{
|
||||
_csv.WriteField(genericRecord.Date);
|
||||
_csv.WriteField(genericRecord.Description);
|
||||
_csv.WriteField(genericRecord.Cost);
|
||||
_csv.WriteField(genericRecord.Notes);
|
||||
_csv.WriteField(genericRecord.Odometer);
|
||||
_csv.WriteField(genericRecord.Tags);
|
||||
foreach (string extraHeader in extraHeaders)
|
||||
{
|
||||
var extraField = genericRecord.ExtraFields.Where(x => x.Name == extraHeader).FirstOrDefault();
|
||||
_csv.WriteField(extraField != null ? extraField.Value : string.Empty);
|
||||
}
|
||||
_csv.NextRecord();
|
||||
}
|
||||
}
|
||||
public static void WriteOdometerRecordExportModel(CsvWriter _csv, IEnumerable<OdometerRecordExportModel> genericRecords)
|
||||
{
|
||||
var extraHeaders = genericRecords.SelectMany(x => x.ExtraFields).Select(y => y.Name).Distinct();
|
||||
//write headers
|
||||
_csv.WriteField(nameof(OdometerRecordExportModel.Date));
|
||||
_csv.WriteField(nameof(OdometerRecordExportModel.InitialOdometer));
|
||||
_csv.WriteField(nameof(OdometerRecordExportModel.Odometer));
|
||||
_csv.WriteField(nameof(OdometerRecordExportModel.Notes));
|
||||
_csv.WriteField(nameof(OdometerRecordExportModel.Tags));
|
||||
foreach (string extraHeader in extraHeaders)
|
||||
{
|
||||
_csv.WriteField($"extrafield_{extraHeader}");
|
||||
}
|
||||
_csv.NextRecord();
|
||||
foreach (OdometerRecordExportModel genericRecord in genericRecords)
|
||||
{
|
||||
_csv.WriteField(genericRecord.Date);
|
||||
_csv.WriteField(genericRecord.InitialOdometer);
|
||||
_csv.WriteField(genericRecord.Odometer);
|
||||
_csv.WriteField(genericRecord.Notes);
|
||||
_csv.WriteField(genericRecord.Tags);
|
||||
foreach (string extraHeader in extraHeaders)
|
||||
{
|
||||
var extraField = genericRecord.ExtraFields.Where(x => x.Name == extraHeader).FirstOrDefault();
|
||||
_csv.WriteField(extraField != null ? extraField.Value : string.Empty);
|
||||
}
|
||||
_csv.NextRecord();
|
||||
}
|
||||
}
|
||||
public static void WriteTaxRecordExportModel(CsvWriter _csv, IEnumerable<TaxRecordExportModel> genericRecords)
|
||||
{
|
||||
var extraHeaders = genericRecords.SelectMany(x => x.ExtraFields).Select(y => y.Name).Distinct();
|
||||
//write headers
|
||||
_csv.WriteField(nameof(TaxRecordExportModel.Date));
|
||||
_csv.WriteField(nameof(TaxRecordExportModel.Description));
|
||||
_csv.WriteField(nameof(TaxRecordExportModel.Cost));
|
||||
_csv.WriteField(nameof(TaxRecordExportModel.Notes));
|
||||
_csv.WriteField(nameof(TaxRecordExportModel.Tags));
|
||||
foreach (string extraHeader in extraHeaders)
|
||||
{
|
||||
_csv.WriteField($"extrafield_{extraHeader}");
|
||||
}
|
||||
_csv.NextRecord();
|
||||
foreach (TaxRecordExportModel genericRecord in genericRecords)
|
||||
{
|
||||
_csv.WriteField(genericRecord.Date);
|
||||
_csv.WriteField(genericRecord.Description);
|
||||
_csv.WriteField(genericRecord.Cost);
|
||||
_csv.WriteField(genericRecord.Notes);
|
||||
_csv.WriteField(genericRecord.Tags);
|
||||
foreach (string extraHeader in extraHeaders)
|
||||
{
|
||||
var extraField = genericRecord.ExtraFields.Where(x => x.Name == extraHeader).FirstOrDefault();
|
||||
_csv.WriteField(extraField != null ? extraField.Value : string.Empty);
|
||||
}
|
||||
_csv.NextRecord();
|
||||
}
|
||||
}
|
||||
public static void WriteSupplyRecordExportModel(CsvWriter _csv, IEnumerable<SupplyRecordExportModel> genericRecords)
|
||||
{
|
||||
var extraHeaders = genericRecords.SelectMany(x => x.ExtraFields).Select(y => y.Name).Distinct();
|
||||
//write headers
|
||||
_csv.WriteField(nameof(SupplyRecordExportModel.Date));
|
||||
_csv.WriteField(nameof(SupplyRecordExportModel.PartNumber));
|
||||
_csv.WriteField(nameof(SupplyRecordExportModel.PartSupplier));
|
||||
_csv.WriteField(nameof(SupplyRecordExportModel.PartQuantity));
|
||||
_csv.WriteField(nameof(SupplyRecordExportModel.Description));
|
||||
_csv.WriteField(nameof(SupplyRecordExportModel.Notes));
|
||||
_csv.WriteField(nameof(SupplyRecordExportModel.Cost));
|
||||
_csv.WriteField(nameof(SupplyRecordExportModel.Tags));
|
||||
foreach (string extraHeader in extraHeaders)
|
||||
{
|
||||
_csv.WriteField($"extrafield_{extraHeader}");
|
||||
}
|
||||
_csv.NextRecord();
|
||||
foreach (SupplyRecordExportModel genericRecord in genericRecords)
|
||||
{
|
||||
_csv.WriteField(genericRecord.Date);
|
||||
_csv.WriteField(genericRecord.PartNumber);
|
||||
_csv.WriteField(genericRecord.PartSupplier);
|
||||
_csv.WriteField(genericRecord.PartQuantity);
|
||||
_csv.WriteField(genericRecord.Description);
|
||||
_csv.WriteField(genericRecord.Notes);
|
||||
_csv.WriteField(genericRecord.Cost);
|
||||
_csv.WriteField(genericRecord.Tags);
|
||||
foreach (string extraHeader in extraHeaders)
|
||||
{
|
||||
var extraField = genericRecord.ExtraFields.Where(x => x.Name == extraHeader).FirstOrDefault();
|
||||
_csv.WriteField(extraField != null ? extraField.Value : string.Empty);
|
||||
}
|
||||
_csv.NextRecord();
|
||||
}
|
||||
}
|
||||
public static void WritePlanRecordExportModel(CsvWriter _csv, IEnumerable<PlanRecordExportModel> genericRecords)
|
||||
{
|
||||
var extraHeaders = genericRecords.SelectMany(x => x.ExtraFields).Select(y => y.Name).Distinct();
|
||||
//write headers
|
||||
_csv.WriteField(nameof(PlanRecordExportModel.DateCreated));
|
||||
_csv.WriteField(nameof(PlanRecordExportModel.DateModified));
|
||||
_csv.WriteField(nameof(PlanRecordExportModel.Description));
|
||||
_csv.WriteField(nameof(PlanRecordExportModel.Notes));
|
||||
_csv.WriteField(nameof(PlanRecordExportModel.Type));
|
||||
_csv.WriteField(nameof(PlanRecordExportModel.Priority));
|
||||
_csv.WriteField(nameof(PlanRecordExportModel.Progress));
|
||||
_csv.WriteField(nameof(PlanRecordExportModel.Cost));
|
||||
foreach (string extraHeader in extraHeaders)
|
||||
{
|
||||
_csv.WriteField($"extrafield_{extraHeader}");
|
||||
}
|
||||
_csv.NextRecord();
|
||||
foreach (PlanRecordExportModel genericRecord in genericRecords)
|
||||
{
|
||||
_csv.WriteField(genericRecord.DateCreated);
|
||||
_csv.WriteField(genericRecord.DateModified);
|
||||
_csv.WriteField(genericRecord.Description);
|
||||
_csv.WriteField(genericRecord.Notes);
|
||||
_csv.WriteField(genericRecord.Type);
|
||||
_csv.WriteField(genericRecord.Priority);
|
||||
_csv.WriteField(genericRecord.Progress);
|
||||
_csv.WriteField(genericRecord.Cost);
|
||||
foreach (string extraHeader in extraHeaders)
|
||||
{
|
||||
var extraField = genericRecord.ExtraFields.Where(x => x.Name == extraHeader).FirstOrDefault();
|
||||
_csv.WriteField(extraField != null ? extraField.Value : string.Empty);
|
||||
}
|
||||
_csv.NextRecord();
|
||||
}
|
||||
}
|
||||
public static string HideZeroCost(string input, bool hideZero, string decorations = "")
|
||||
{
|
||||
if (input == 0M.ToString("C2") && hideZero)
|
||||
{
|
||||
return "---";
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(decorations) ? input : $"{input}{decorations}";
|
||||
}
|
||||
}
|
||||
public static string HideZeroCost(decimal input, bool hideZero, string decorations = "")
|
||||
{
|
||||
if (input == default && hideZero)
|
||||
{
|
||||
return "---";
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(decorations) ? input.ToString("C2") : $"{input.ToString("C2")}{decorations}";
|
||||
}
|
||||
}
|
||||
public static string GetIconByFileExtension(string fileLocation)
|
||||
{
|
||||
var fileExt = Path.GetExtension(fileLocation);
|
||||
if (!fileLocation.StartsWith("/documents") && !fileLocation.StartsWith("documents") && !fileLocation.StartsWith("/temp") && !fileLocation.StartsWith("temp"))
|
||||
{
|
||||
return "bi-link-45deg";
|
||||
}
|
||||
switch (fileExt)
|
||||
{
|
||||
case ".pdf":
|
||||
return "bi-file-earmark-pdf";
|
||||
case ".zip":
|
||||
case ".7z":
|
||||
case ".rar":
|
||||
return "bi-file-earmark-zip";
|
||||
case ".png":
|
||||
case ".jpg":
|
||||
case ".jpeg":
|
||||
return "bi-file-earmark-image";
|
||||
case ".xls":
|
||||
case ".xlsx":
|
||||
case ".xlsm":
|
||||
case ".ods":
|
||||
case ".csv":
|
||||
return "bi-file-earmark-spreadsheet";
|
||||
case ".docx":
|
||||
case ".odt":
|
||||
case ".rtf":
|
||||
return "bi-file-earmark-richtext";
|
||||
default:
|
||||
return "bi-file-earmark";
|
||||
}
|
||||
}
|
||||
public static JsonSerializerOptions GetInvariantOption()
|
||||
{
|
||||
var serializerOption = new JsonSerializerOptions();
|
||||
serializerOption.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
|
||||
serializerOption.Converters.Add(new InvariantConverter());
|
||||
return serializerOption;
|
||||
}
|
||||
public static void WriteGasRecordExportModel(CsvWriter _csv, IEnumerable<GasRecordExportModel> genericRecords)
|
||||
{
|
||||
var extraHeaders = genericRecords.SelectMany(x => x.ExtraFields).Select(y => y.Name).Distinct();
|
||||
//write headers
|
||||
_csv.WriteField(nameof(GasRecordExportModel.Date));
|
||||
_csv.WriteField(nameof(GasRecordExportModel.Odometer));
|
||||
_csv.WriteField(nameof(GasRecordExportModel.FuelConsumed));
|
||||
_csv.WriteField(nameof(GasRecordExportModel.Cost));
|
||||
_csv.WriteField(nameof(GasRecordExportModel.FuelEconomy));
|
||||
_csv.WriteField(nameof(GasRecordExportModel.IsFillToFull));
|
||||
_csv.WriteField(nameof(GasRecordExportModel.MissedFuelUp));
|
||||
_csv.WriteField(nameof(GasRecordExportModel.Notes));
|
||||
_csv.WriteField(nameof(GasRecordExportModel.Tags));
|
||||
foreach (string extraHeader in extraHeaders)
|
||||
{
|
||||
_csv.WriteField($"extrafield_{extraHeader}");
|
||||
}
|
||||
_csv.NextRecord();
|
||||
foreach (GasRecordExportModel genericRecord in genericRecords)
|
||||
{
|
||||
_csv.WriteField(genericRecord.Date);
|
||||
_csv.WriteField(genericRecord.Odometer);
|
||||
_csv.WriteField(genericRecord.FuelConsumed);
|
||||
_csv.WriteField(genericRecord.Cost);
|
||||
_csv.WriteField(genericRecord.FuelEconomy);
|
||||
_csv.WriteField(genericRecord.IsFillToFull);
|
||||
_csv.WriteField(genericRecord.MissedFuelUp);
|
||||
_csv.WriteField(genericRecord.Notes);
|
||||
_csv.WriteField(genericRecord.Tags);
|
||||
foreach (string extraHeader in extraHeaders)
|
||||
{
|
||||
var extraField = genericRecord.ExtraFields.Where(x => x.Name == extraHeader).FirstOrDefault();
|
||||
_csv.WriteField(extraField != null ? extraField.Value : string.Empty);
|
||||
}
|
||||
_csv.NextRecord();
|
||||
}
|
||||
}
|
||||
public static byte[] RemindersToCalendar(List<ReminderRecordViewModel> reminders)
|
||||
{
|
||||
//converts reminders to iCal file
|
||||
StringBuilder sb = new StringBuilder();
|
||||
//start the calendar item
|
||||
sb.AppendLine("BEGIN:VCALENDAR");
|
||||
sb.AppendLine("VERSION:2.0");
|
||||
sb.AppendLine("PRODID:motovaultpro.com");
|
||||
sb.AppendLine("CALSCALE:GREGORIAN");
|
||||
sb.AppendLine("METHOD:PUBLISH");
|
||||
|
||||
//create events.
|
||||
foreach(ReminderRecordViewModel reminder in reminders)
|
||||
{
|
||||
var dtStart = reminder.Date.Date.ToString("yyyyMMddTHHmm00");
|
||||
var dtEnd = reminder.Date.Date.AddDays(1).AddMilliseconds(-1).ToString("yyyyMMddTHHmm00");
|
||||
var calendarUID = new Guid(MD5.HashData(Encoding.UTF8.GetBytes($"{dtStart}_{reminder.Description}")));
|
||||
sb.AppendLine("BEGIN:VEVENT");
|
||||
sb.AppendLine("DTSTAMP:" + DateTime.Now.ToString("yyyyMMddTHHmm00"));
|
||||
sb.AppendLine("UID:" + calendarUID);
|
||||
sb.AppendLine("DTSTART:" + dtStart);
|
||||
sb.AppendLine("DTEND:" + dtEnd);
|
||||
sb.AppendLine($"SUMMARY:{reminder.Description}");
|
||||
sb.AppendLine($"DESCRIPTION:{reminder.Description}");
|
||||
switch (reminder.Urgency)
|
||||
{
|
||||
case ReminderUrgency.NotUrgent:
|
||||
sb.AppendLine("PRIORITY:3");
|
||||
break;
|
||||
case ReminderUrgency.Urgent:
|
||||
sb.AppendLine("PRIORITY:2");
|
||||
break;
|
||||
case ReminderUrgency.VeryUrgent:
|
||||
sb.AppendLine("PRIORITY:1");
|
||||
break;
|
||||
case ReminderUrgency.PastDue:
|
||||
sb.AppendLine("PRIORITY:1");
|
||||
break;
|
||||
}
|
||||
sb.AppendLine("END:VEVENT");
|
||||
}
|
||||
|
||||
//end calendar item
|
||||
sb.AppendLine("END:VCALENDAR");
|
||||
string calendarContent = sb.ToString();
|
||||
return Encoding.UTF8.GetBytes(calendarContent);
|
||||
}
|
||||
public static decimal CalculateNiceStepSize(decimal min, decimal max, int desiredTicks)
|
||||
{
|
||||
double range = Convert.ToDouble(max - min);
|
||||
double roughStep = range / desiredTicks;
|
||||
double exponent = Math.Floor(Math.Log10(roughStep));
|
||||
double stepPower = Math.Pow(10, exponent);
|
||||
double normalizedStep = roughStep / stepPower;
|
||||
|
||||
// Choose the closest nice interval (1, 2, or 5)
|
||||
double[] niceSteps = { 1, 2, 5 };
|
||||
double goodNormalizedStep = niceSteps.OrderBy(s => Math.Abs(s - normalizedStep)).First();
|
||||
|
||||
return Convert.ToDecimal(goodNormalizedStep * stepPower);
|
||||
}
|
||||
}
|
||||
}
|
||||
197
Helper/TranslationHelper.cs
Normal file
197
Helper/TranslationHelper.cs
Normal file
@@ -0,0 +1,197 @@
|
||||
using MotoVaultPro.Models;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace MotoVaultPro.Helper
|
||||
{
|
||||
public interface ITranslationHelper
|
||||
{
|
||||
string Translate(string userLanguage, string text);
|
||||
Dictionary<string, string> GetTranslations(string userLanguage);
|
||||
OperationResponse SaveTranslation(string userLanguage, Dictionary<string, string> translations);
|
||||
string ExportTranslation(Dictionary<string, string> translations);
|
||||
}
|
||||
public class TranslationHelper : ITranslationHelper
|
||||
{
|
||||
private readonly IFileHelper _fileHelper;
|
||||
private readonly IConfiguration _config;
|
||||
private readonly ILogger<ITranslationHelper> _logger;
|
||||
private IMemoryCache _cache;
|
||||
public TranslationHelper(IFileHelper fileHelper, IConfiguration config, IMemoryCache memoryCache, ILogger<ITranslationHelper> logger)
|
||||
{
|
||||
_fileHelper = fileHelper;
|
||||
_config = config;
|
||||
_cache = memoryCache;
|
||||
_logger = logger;
|
||||
}
|
||||
public string Translate(string userLanguage, string text)
|
||||
{
|
||||
bool create = bool.Parse(_config["LUBELOGGER_TRANSLATOR"] ?? "false");
|
||||
//transform input text into key.
|
||||
string translationKey = text.Replace(" ", "_");
|
||||
var translationFilePath = userLanguage == "en_US" ? _fileHelper.GetFullFilePath($"/defaults/en_US.json") : _fileHelper.GetFullFilePath($"/translations/{userLanguage}.json", false);
|
||||
var dictionary = _cache.GetOrCreate<Dictionary<string, string>>($"lang_{userLanguage}", entry =>
|
||||
{
|
||||
entry.SlidingExpiration = TimeSpan.FromHours(1);
|
||||
if (File.Exists(translationFilePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
var translationFile = File.ReadAllText(translationFilePath);
|
||||
var translationDictionary = JsonSerializer.Deserialize<Dictionary<string, string>>(translationFile);
|
||||
return translationDictionary ?? new Dictionary<string, string>();
|
||||
} catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.Message);
|
||||
return new Dictionary<string, string>();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogError($"Could not find translation file for {userLanguage}");
|
||||
return new Dictionary<string, string>();
|
||||
}
|
||||
});
|
||||
if (dictionary != null && dictionary.ContainsKey(translationKey))
|
||||
{
|
||||
return dictionary[translationKey];
|
||||
}
|
||||
else if (create && File.Exists(translationFilePath))
|
||||
{
|
||||
//create entry
|
||||
dictionary.Add(translationKey, text);
|
||||
_logger.LogInformation($"Translation key added to {userLanguage} for {translationKey} with value {text}");
|
||||
File.WriteAllText(translationFilePath, JsonSerializer.Serialize(dictionary));
|
||||
return text;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
private Dictionary<string, string> GetDefaultTranslation()
|
||||
{
|
||||
//this method always returns en_US translation.
|
||||
var translationFilePath = _fileHelper.GetFullFilePath($"/defaults/en_US.json");
|
||||
if (!string.IsNullOrWhiteSpace(translationFilePath))
|
||||
{
|
||||
//file exists.
|
||||
try
|
||||
{
|
||||
var translationFile = File.ReadAllText(translationFilePath);
|
||||
var translationDictionary = JsonSerializer.Deserialize<Dictionary<string, string>>(translationFile);
|
||||
return translationDictionary ?? new Dictionary<string, string>();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.Message);
|
||||
return new Dictionary<string, string>();
|
||||
}
|
||||
}
|
||||
_logger.LogError($"Could not find translation file for en_US");
|
||||
return new Dictionary<string, string>();
|
||||
}
|
||||
public Dictionary<string, string> GetTranslations(string userLanguage)
|
||||
{
|
||||
var defaultTranslation = GetDefaultTranslation();
|
||||
if (userLanguage == "en_US")
|
||||
{
|
||||
return defaultTranslation;
|
||||
}
|
||||
var translationFilePath = _fileHelper.GetFullFilePath($"/translations/{userLanguage}.json");
|
||||
if (!string.IsNullOrWhiteSpace(translationFilePath))
|
||||
{
|
||||
//file exists.
|
||||
try
|
||||
{
|
||||
var translationFile = File.ReadAllText(translationFilePath);
|
||||
var translationDictionary = JsonSerializer.Deserialize<Dictionary<string, string>>(translationFile);
|
||||
if (translationDictionary != null)
|
||||
{
|
||||
foreach(var translation in translationDictionary)
|
||||
{
|
||||
if (defaultTranslation.ContainsKey(translation.Key))
|
||||
{
|
||||
defaultTranslation[translation.Key] = translation.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultTranslation ?? new Dictionary<string, string>();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.Message);
|
||||
return new Dictionary<string, string>();
|
||||
}
|
||||
}
|
||||
_logger.LogError($"Could not find translation file for {userLanguage}");
|
||||
return new Dictionary<string, string>();
|
||||
}
|
||||
public OperationResponse SaveTranslation(string userLanguage, Dictionary<string, string> translations)
|
||||
{
|
||||
bool create = bool.Parse(_config["LUBELOGGER_TRANSLATOR"] ?? "false");
|
||||
bool isDefaultLanguage = userLanguage == "en_US";
|
||||
if (isDefaultLanguage && !create)
|
||||
{
|
||||
return OperationResponse.Failed("The translation file name en_US is reserved.");
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(userLanguage))
|
||||
{
|
||||
return OperationResponse.Failed("File name is not provided.");
|
||||
}
|
||||
if (!translations.Any())
|
||||
{
|
||||
return OperationResponse.Failed("Translation has no data.");
|
||||
}
|
||||
var translationFilePath = isDefaultLanguage ? _fileHelper.GetFullFilePath($"/defaults/en_US.json") : _fileHelper.GetFullFilePath($"/translations/{userLanguage}.json", false);
|
||||
try
|
||||
{
|
||||
if (File.Exists(translationFilePath))
|
||||
{
|
||||
//write to file
|
||||
File.WriteAllText(translationFilePath, JsonSerializer.Serialize(translations));
|
||||
_cache.Remove($"lang_{userLanguage}"); //clear out cache, force a reload from file.
|
||||
} else
|
||||
{
|
||||
//check if directory exists first.
|
||||
var translationDirectory = _fileHelper.GetFullFilePath("translations/", false);
|
||||
if (!Directory.Exists(translationDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(translationDirectory);
|
||||
}
|
||||
//write to file
|
||||
File.WriteAllText(translationFilePath, JsonSerializer.Serialize(translations));
|
||||
}
|
||||
return OperationResponse.Succeed("Translation Updated");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.Message);
|
||||
return OperationResponse.Failed();
|
||||
}
|
||||
}
|
||||
public string ExportTranslation(Dictionary<string, string> translations)
|
||||
{
|
||||
try
|
||||
{
|
||||
var tempFileName = $"/temp/{Guid.NewGuid()}.json";
|
||||
string uploadDirectory = _fileHelper.GetFullFilePath("temp/", false);
|
||||
if (!Directory.Exists(uploadDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(uploadDirectory);
|
||||
}
|
||||
var saveFilePath = _fileHelper.GetFullFilePath(tempFileName, false);
|
||||
//standardize translation format for export only.
|
||||
Dictionary<string, string> sortedTranslations = new Dictionary<string, string>();
|
||||
foreach (var translation in translations.OrderBy(x => x.Key))
|
||||
{
|
||||
sortedTranslations.Add(translation.Key, translation.Value);
|
||||
};
|
||||
File.WriteAllText(saveFilePath, JsonSerializer.Serialize(sortedTranslations, new JsonSerializerOptions { WriteIndented = true }));
|
||||
return tempFileName;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.Message);
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user