case 4632

This commit is contained in:
2025-02-18 21:08:56 +00:00
parent 406e42df4e
commit 9d7c575dd1
5 changed files with 813 additions and 345 deletions

View File

@@ -6,15 +6,21 @@ If you are viewing this page from your local copy of AyaNova be advised that it
See the [upgrade instructions](ops-upgrade.md) section of this manual for details. See the [upgrade instructions](ops-upgrade.md) section of this manual for details.
## 2024 ## 2025
### AyaNova 8.2.3 ### AyaNova 8.2.3
In progress... Released 2025-02-18
**Fixed**
- Server: resolved edge case issue with Customer Notifications attached reports could fail to generate when platform is Windows / IIS and AYANOVA_REPORT_RENDER_API_URL_OVERRIDE configuration setting override in effect
- Docs: fixed outdated and missing installation steps for setting directory rights and ownership in [linux server install](ops-install-linux-server.md) for post 8.2.0 changes (hat tip to Steven L. for spotting this) - Docs: fixed outdated and missing installation steps for setting directory rights and ownership in [linux server install](ops-install-linux-server.md) for post 8.2.0 changes (hat tip to Steven L. for spotting this)
- Docs: improved PO part requests documentation section to specify that only Parts that match the Vendor selected on the PO will be offered for selection in the Part Requests list. - Docs: improved PO part requests documentation section to specify that only Parts that match the Vendor selected on the PO will be offered for selection in the Part Requests list
## 2024
### AyaNova 8.2.2 ### AyaNova 8.2.2

View File

@@ -8,7 +8,7 @@ site_name: AyaNova manual
site_dir: "../../../server/AyaNova/wwwroot/docs" site_dir: "../../../server/AyaNova/wwwroot/docs"
site_url: https://ayanova.com/docs/ site_url: https://ayanova.com/docs/
strict: true strict: true
copyright: Copyright © 2022-2025 Ground Zero Tech-Works Inc. REV-2025-02-10 copyright: Copyright © 2022-2025 Ground Zero Tech-Works Inc. REV-2025-02-18
extra: extra:
generator: false generator: false
# Extensions # Extensions

File diff suppressed because it is too large Load Diff

View File

@@ -1,17 +1,16 @@
using System; using System;
using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using AyaNova.Models; using AyaNova.Models;
using AyaNova.Util; using AyaNova.Util;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using System.Text.RegularExpressions;
namespace AyaNova.Biz namespace AyaNova.Biz
{ {
/// <summary> /// <summary>
/// Notification processor /// Notification processor
/// turn notifyEvent records into inappnotification records for in app viewing and / or deliver smtp notifications seperately /// turn notifyEvent records into inappnotification records for in app viewing and / or deliver smtp notifications seperately
@@ -20,13 +19,15 @@ namespace AyaNova.Biz
internal static class CoreJobCustomerNotify internal static class CoreJobCustomerNotify
{ {
private static bool NotifyIsRunning = false; private static bool NotifyIsRunning = false;
private static ILogger log = AyaNova.Util.ApplicationLogging.CreateLogger("CoreJobCustomerNotify"); private static ILogger log = AyaNova.Util.ApplicationLogging.CreateLogger(
"CoreJobCustomerNotify"
);
private static DateTime lastRun = DateTime.MinValue; private static DateTime lastRun = DateTime.MinValue;
#if (DEBUG) #if (DEBUG)
private static TimeSpan RUN_EVERY_INTERVAL = new TimeSpan(0, 0, 21);//no more frequently than once every 20 seconds private static TimeSpan RUN_EVERY_INTERVAL = new TimeSpan(0, 0, 21); //no more frequently than once every 20 seconds
#else #else
private static TimeSpan RUN_EVERY_INTERVAL = new TimeSpan(0, 1, 1);//no more frequently than once every 1 minute private static TimeSpan RUN_EVERY_INTERVAL = new TimeSpan(0, 1, 1); //no more frequently than once every 1 minute
#endif #endif
//////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////
@@ -43,7 +44,9 @@ namespace AyaNova.Biz
//This will get triggered roughly every minute, but we don't want to deliver that frequently //This will get triggered roughly every minute, but we don't want to deliver that frequently
if (DateTime.UtcNow - lastRun < RUN_EVERY_INTERVAL) if (DateTime.UtcNow - lastRun < RUN_EVERY_INTERVAL)
{ {
log.LogTrace($"CustomerNotify ran less than {RUN_EVERY_INTERVAL} ago, exiting this cycle"); log.LogTrace(
$"CustomerNotify ran less than {RUN_EVERY_INTERVAL} ago, exiting this cycle"
);
return; return;
} }
try try
@@ -54,18 +57,31 @@ namespace AyaNova.Biz
using (AyContext ct = AyaNova.Util.ServiceProviderProvider.DBContext) using (AyContext ct = AyaNova.Util.ServiceProviderProvider.DBContext)
{ {
var customerevents = await ct.CustomerNotifyEvent.AsNoTracking().ToListAsync(); var customerevents = await ct.CustomerNotifyEvent.AsNoTracking().ToListAsync();
log.LogDebug($"Found {customerevents.Count} CustomerNotifyEvents to examine for potential delivery"); log.LogDebug(
$"Found {customerevents.Count} CustomerNotifyEvents to examine for potential delivery"
);
//iterate and deliver //iterate and deliver
foreach (var customernotifyevent in customerevents) foreach (var customernotifyevent in customerevents)
{ {
//no notifications for inactive users, just delete it as if it was delivered //no notifications for inactive users, just delete it as if it was delivered
var CustInfo = await ct.Customer.AsNoTracking().Where(x => x.Id == customernotifyevent.CustomerId).Select(x => new { x.Name, x.Active, x.Tags, x.EmailAddress }).FirstOrDefaultAsync(); var CustInfo = await ct
.Customer.AsNoTracking()
.Where(x => x.Id == customernotifyevent.CustomerId)
.Select(x => new
{
x.Name,
x.Active,
x.Tags,
x.EmailAddress,
})
.FirstOrDefaultAsync();
if (!CustInfo.Active) if (!CustInfo.Active)
{ {
log.LogDebug($"Inactive Customer {CustInfo.Name}, removing notify rather than delivering it: {customernotifyevent}"); log.LogDebug(
$"Inactive Customer {CustInfo.Name}, removing notify rather than delivering it: {customernotifyevent}"
);
ct.CustomerNotifyEvent.Remove(customernotifyevent); ct.CustomerNotifyEvent.Remove(customernotifyevent);
await ct.SaveChangesAsync(); await ct.SaveChangesAsync();
continue; continue;
@@ -73,29 +89,42 @@ namespace AyaNova.Biz
if (string.IsNullOrWhiteSpace(CustInfo.EmailAddress)) if (string.IsNullOrWhiteSpace(CustInfo.EmailAddress))
{ {
log.LogDebug($"Customer {CustInfo.Name} has no email address, removing notify rather than delivering it: {customernotifyevent}"); log.LogDebug(
$"Customer {CustInfo.Name} has no email address, removing notify rather than delivering it: {customernotifyevent}"
);
ct.CustomerNotifyEvent.Remove(customernotifyevent); ct.CustomerNotifyEvent.Remove(customernotifyevent);
await ct.SaveChangesAsync(); await ct.SaveChangesAsync();
continue; continue;
} }
//Get subscription for delivery //Get subscription for delivery
var Subscription = await ct.CustomerNotifySubscription.AsNoTracking().FirstOrDefaultAsync(x => x.Id == customernotifyevent.CustomerNotifySubscriptionId); var Subscription = await ct
.CustomerNotifySubscription.AsNoTracking()
.FirstOrDefaultAsync(x =>
x.Id == customernotifyevent.CustomerNotifySubscriptionId
);
//NOTE: There is no need to separate out future delivery and immediate delivery because //NOTE: There is no need to separate out future delivery and immediate delivery because
// All events have an event date, it's either immediate upon creation or it's future // All events have an event date, it's either immediate upon creation or it's future
// but not all events have an age value including ones with future event dates, // but not all events have an age value including ones with future event dates,
// and the default agevalue and advancenotice are both zero regardless so the block below works for either future or immediate deliveries // and the default agevalue and advancenotice are both zero regardless so the block below works for either future or immediate deliveries
var deliverAfter = customernotifyevent.EventDate + Subscription.AgeValue - Subscription.AdvanceNotice; var deliverAfter =
customernotifyevent.EventDate
+ Subscription.AgeValue
- Subscription.AdvanceNotice;
if (deliverAfter < DateTime.UtcNow) if (deliverAfter < DateTime.UtcNow)
{ {
//Do the delivery, it's kosher //Do the delivery, it's kosher
await DeliverCustomerNotificationSMTP(customernotifyevent, Subscription, CustInfo.EmailAddress, ct); await DeliverCustomerNotificationSMTP(
customernotifyevent,
Subscription,
CustInfo.EmailAddress,
ct
);
} }
} }
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -104,25 +133,28 @@ namespace AyaNova.Biz
} }
finally finally
{ {
log.LogDebug("CustomerNotify is done setting to not running state and tagging lastRun timestamp"); log.LogDebug(
"CustomerNotify is done setting to not running state and tagging lastRun timestamp"
);
lastRun = DateTime.UtcNow; lastRun = DateTime.UtcNow;
NotifyIsRunning = false; NotifyIsRunning = false;
} }
} }
//=== //===
private static async Task DeliverCustomerNotificationSMTP(CustomerNotifyEvent ne, CustomerNotifySubscription subscription, string deliveryAddress, AyContext ct) private static async Task DeliverCustomerNotificationSMTP(
CustomerNotifyEvent ne,
CustomerNotifySubscription subscription,
string deliveryAddress,
AyContext ct
)
{ {
var DeliveryLogItem = new CustomerNotifyDeliveryLog() var DeliveryLogItem = new CustomerNotifyDeliveryLog()
{ {
Processed = DateTime.UtcNow, Processed = DateTime.UtcNow,
ObjectId = ne.ObjectId, ObjectId = ne.ObjectId,
CustomerNotifySubscriptionId = ne.CustomerNotifySubscriptionId, CustomerNotifySubscriptionId = ne.CustomerNotifySubscriptionId,
Fail = false Fail = false,
}; };
try try
@@ -131,21 +163,25 @@ namespace AyaNova.Biz
if (string.IsNullOrWhiteSpace(deliveryAddress)) if (string.IsNullOrWhiteSpace(deliveryAddress))
{ {
DeliveryLogItem.Fail = true; DeliveryLogItem.Fail = true;
DeliveryLogItem.Error = $"No email address provided for smtp delivery; event: {ne}"; DeliveryLogItem.Error =
$"No email address provided for smtp delivery; event: {ne}";
} }
else else
{ {
if (!ServerGlobalOpsSettingsCache.Notify.SmtpDeliveryActive) if (!ServerGlobalOpsSettingsCache.Notify.SmtpDeliveryActive)
{ {
await NotifyEventHelper.AddOpsProblemEvent($"Email notifications are set to OFF at server, unable to send Customer email notification for this event:{ne}"); await NotifyEventHelper.AddOpsProblemEvent(
log.LogInformation($"** WARNING: SMTP notification is currently set to Active=False; unable to deliver Customer email notification, [CustomerId={ne.CustomerId}, Customer Notify subscription={ne.CustomerNotifySubscriptionId}]. Change this setting or remove all Customer notifications if this is permanent **"); $"Email notifications are set to OFF at server, unable to send Customer email notification for this event:{ne}"
);
log.LogInformation(
$"** WARNING: SMTP notification is currently set to Active=False; unable to deliver Customer email notification, [CustomerId={ne.CustomerId}, Customer Notify subscription={ne.CustomerNotifySubscriptionId}]. Change this setting or remove all Customer notifications if this is permanent **"
);
DeliveryLogItem.Fail = true; DeliveryLogItem.Fail = true;
DeliveryLogItem.Error = $"Email notifications are set to OFF at server, unable to send Customer email notification for this event: {ne}"; DeliveryLogItem.Error =
$"Email notifications are set to OFF at server, unable to send Customer email notification for this event: {ne}";
} }
else else
{ {
//BUILD SUBJECT AND BODY FROM TOKENS IF REQUIRED //BUILD SUBJECT AND BODY FROM TOKENS IF REQUIRED
var Subject = subscription.Subject; var Subject = subscription.Subject;
var Body = subscription.Template; var Body = subscription.Template;
@@ -157,54 +193,98 @@ namespace AyaNova.Biz
{ {
case AyaType.Quote: case AyaType.Quote:
{ {
var qt = await ct.Quote.AsNoTracking().FirstOrDefaultAsync(z => z.Id == ne.ObjectId); var qt = await ct
.Quote.AsNoTracking()
.FirstOrDefaultAsync(z => z.Id == ne.ObjectId);
if (qt == null) if (qt == null)
{ {
//maybe deleted, this can't proceed //maybe deleted, this can't proceed
throw new ApplicationException($"Unable to make delivery for customer notify event as Quote {ne.Name} was not found during delivery, deleted?"); throw new ApplicationException(
$"Unable to make delivery for customer notify event as Quote {ne.Name} was not found during delivery, deleted?"
);
} }
var CustomerName = await ct.Customer.AsNoTracking().Where(x => x.Id == qt.CustomerId).Select(x => x.Name).FirstOrDefaultAsync(); var CustomerName = await ct
.Customer.AsNoTracking()
.Where(x => x.Id == qt.CustomerId)
.Select(x => x.Name)
.FirstOrDefaultAsync();
Subject = SetQuoteTokens(Subject, qt, CustomerName); Subject = SetQuoteTokens(Subject, qt, CustomerName);
Body = SetQuoteTokens(Body, qt, CustomerName); Body = SetQuoteTokens(Body, qt, CustomerName);
} }
break; break;
case AyaType.WorkOrder: case AyaType.WorkOrder:
{ {
var wo = await ct.WorkOrder.AsNoTracking().FirstOrDefaultAsync(z => z.Id == ne.ObjectId); var wo = await ct
.WorkOrder.AsNoTracking()
.FirstOrDefaultAsync(z => z.Id == ne.ObjectId);
if (wo == null) if (wo == null)
{ {
//maybe deleted, this can't proceed //maybe deleted, this can't proceed
throw new ApplicationException($"Unable to make delivery for customer notify event as WorkOrder {ne.Name} was not found during delivery, deleted?"); throw new ApplicationException(
$"Unable to make delivery for customer notify event as WorkOrder {ne.Name} was not found during delivery, deleted?"
);
} }
var CustomerName = await ct.Customer.AsNoTracking().Where(x => x.Id == wo.CustomerId).Select(x => x.Name).FirstOrDefaultAsync(); var CustomerName = await ct
var StatusName = await ct.WorkOrderStatus.AsNoTracking().Where(x => x.Id == wo.LastStatusId).Select(x => x.Name).FirstOrDefaultAsync(); .Customer.AsNoTracking()
Subject = SetWorkOrderTokens(Subject, wo, CustomerName, StatusName); .Where(x => x.Id == wo.CustomerId)
Body = SetWorkOrderTokens(Body, wo, CustomerName, StatusName); .Select(x => x.Name)
.FirstOrDefaultAsync();
var StatusName = await ct
.WorkOrderStatus.AsNoTracking()
.Where(x => x.Id == wo.LastStatusId)
.Select(x => x.Name)
.FirstOrDefaultAsync();
Subject = SetWorkOrderTokens(
Subject,
wo,
CustomerName,
StatusName
);
Body = SetWorkOrderTokens(
Body,
wo,
CustomerName,
StatusName
);
} }
break; break;
case AyaType.CustomerServiceRequest: case AyaType.CustomerServiceRequest:
{ {
var csr = await ct.CustomerServiceRequest.AsNoTracking().FirstOrDefaultAsync(z => z.Id == ne.ObjectId); var csr = await ct
.CustomerServiceRequest.AsNoTracking()
.FirstOrDefaultAsync(z => z.Id == ne.ObjectId);
if (csr == null) if (csr == null)
{ {
//maybe deleted, this can't proceed //maybe deleted, this can't proceed
throw new ApplicationException($"Unable to make delivery for customer notify event as CustomerServiceRequest {ne.Name} was not found during delivery, deleted?"); throw new ApplicationException(
$"Unable to make delivery for customer notify event as CustomerServiceRequest {ne.Name} was not found during delivery, deleted?"
);
} }
var CustomerName = await ct.Customer.AsNoTracking().Where(x => x.Id == csr.CustomerId).Select(x => x.Name).FirstOrDefaultAsync(); var CustomerName = await ct
var UserName = await ct.User.AsNoTracking().Where(x => x.Id == csr.RequestedByUserId).Select(x => x.Name).FirstOrDefaultAsync(); .Customer.AsNoTracking()
Subject = SetCSRTokens(Subject, csr, CustomerName, UserName); .Where(x => x.Id == csr.CustomerId)
.Select(x => x.Name)
.FirstOrDefaultAsync();
var UserName = await ct
.User.AsNoTracking()
.Where(x => x.Id == csr.RequestedByUserId)
.Select(x => x.Name)
.FirstOrDefaultAsync();
Subject = SetCSRTokens(
Subject,
csr,
CustomerName,
UserName
);
Body = SetCSRTokens(Body, csr, CustomerName, UserName); Body = SetCSRTokens(Body, csr, CustomerName, UserName);
} }
break; break;
} }
} }
IMailer m = AyaNova.Util.ServiceProviderProvider.Mailer; IMailer m = AyaNova.Util.ServiceProviderProvider.Mailer;
//generate report if applicable //generate report if applicable
bool isReportableEvent = false; bool isReportableEvent = false;
@@ -220,7 +300,12 @@ namespace AyaNova.Biz
{ {
long subTranslationId = (long)subscription.TranslationId; long subTranslationId = (long)subscription.TranslationId;
ReportBiz biz = new ReportBiz(ct, 1, subTranslationId, AuthorizationRoles.BizAdmin); ReportBiz biz = new ReportBiz(
ct,
1,
subTranslationId,
AuthorizationRoles.BizAdmin
);
//example with workorder report //example with workorder report
//{"AType":34,"selectedRowIds":[355],"ReportId":9,"ClientMeta":{"UserName":"AyaNova SuperUser","Authorization":"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOiIxNjQ2NzgyNTc4IiwiaXNzIjoiYXlhbm92YS5jb20iLCJpZCI6IjEifQ.ad7Acq54JCRGitDWKDJFFnqKkidbdaKaFmj-RA_RG5E","DownloadToken":"NdoU8ca3LG4L39Tj2oi3UReeeM7FLevTgbgopTPhGbA","TimeZoneName":"America/Los_Angeles","LanguageName":"en-US","Hour12":true,"CurrencyName":"USD","DefaultLocale":"en","PDFDate":"3/3/22","PDFTime":"3:38 PM"}} //{"AType":34,"selectedRowIds":[355],"ReportId":9,"ClientMeta":{"UserName":"AyaNova SuperUser","Authorization":"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOiIxNjQ2NzgyNTc4IiwiaXNzIjoiYXlhbm92YS5jb20iLCJpZCI6IjEifQ.ad7Acq54JCRGitDWKDJFFnqKkidbdaKaFmj-RA_RG5E","DownloadToken":"NdoU8ca3LG4L39Tj2oi3UReeeM7FLevTgbgopTPhGbA","TimeZoneName":"America/Los_Angeles","LanguageName":"en-US","Hour12":true,"CurrencyName":"USD","DefaultLocale":"en","PDFDate":"3/3/22","PDFTime":"3:38 PM"}}
@@ -235,61 +320,106 @@ namespace AyaNova.Biz
var pdfDate = new DateTime().ToShortDateString(); var pdfDate = new DateTime().ToShortDateString();
var pdfTime = new DateTime().ToShortTimeString(); var pdfTime = new DateTime().ToShortTimeString();
var h12 = subscription.Hour12 ? "true" : "false"; var h12 = subscription.Hour12 ? "true" : "false";
reportRequest.ClientMeta = JToken.Parse($"{{'UserName':'-','Authorization':'Bearer {jwt}','TimeZoneName':'{subscription.TimeZoneOverride}','LanguageName':'{subscription.LanguageOverride}','Hour12':{h12},'CurrencyName':'{subscription.CurrencyName}','DefaultLocale':'en','PDFDate':'{pdfDate}','PDFTime':'{pdfTime}'}}"); reportRequest.ClientMeta = JToken.Parse(
$"{{'UserName':'-','Authorization':'Bearer {jwt}','TimeZoneName':'{subscription.TimeZoneOverride}','LanguageName':'{subscription.LanguageOverride}','Hour12':{h12},'CurrencyName':'{subscription.CurrencyName}','DefaultLocale':'en','PDFDate':'{pdfDate}','PDFTime':'{pdfTime}'}}"
);
//get port number //get port number
var match = System.Text.RegularExpressions.Regex.Match(ServerBootConfig.AYANOVA_USE_URLS, "[0-9]+"); var match = System.Text.RegularExpressions.Regex.Match(
var API_URL = $"http://127.0.0.1:{match.Value}/api/{AyaNovaVersion.CurrentApiVersion}/"; ServerBootConfig.AYANOVA_USE_URLS,
var jobid = await biz.RequestRenderReport(reportRequest, DateTime.UtcNow.AddMinutes(ServerBootConfig.AYANOVA_REPORT_RENDERING_TIMEOUT), API_URL, "CUSTOMER NOTIFICATION - NO USER"); "[0-9]+"
);
var API_URL =
$"http://127.0.0.1:{match.Value}/api/{AyaNovaVersion.CurrentApiVersion}/";
var jobid = await biz.RequestRenderReport(
reportRequest,
DateTime.UtcNow.AddMinutes(
ServerBootConfig.AYANOVA_REPORT_RENDERING_TIMEOUT
),
API_URL,
ServerBootConfig.CUSTOMER_NOTIFICATION_ATTACHED_REPORT_RENDER_USERNAME
);
if (jobid == null) if (jobid == null)
{ {
throw new ApplicationException($"Report render job id is null failed to start"); throw new ApplicationException(
$"Report render job id is null failed to start"
);
} }
else else
{ {
bool done = false; bool done = false;
DateTime bailAfter = DateTime.Now.AddMinutes(ServerBootConfig.AYANOVA_REPORT_RENDERING_TIMEOUT); DateTime bailAfter = DateTime.Now.AddMinutes(
ServerBootConfig.AYANOVA_REPORT_RENDERING_TIMEOUT
);
while (!done && DateTime.Now < bailAfter) while (!done && DateTime.Now < bailAfter)
{ {
var status = await JobsBiz.GetJobStatusAsync((Guid)jobid); var status = await JobsBiz.GetJobStatusAsync((Guid)jobid);
switch (status) switch (status)
{ {
case JobStatus.Completed: case JobStatus.Completed:
{ {
done = true; done = true;
//get job logs and parse file name from it //get job logs and parse file name from it
JobOperationsBiz jobopsbiz = new JobOperationsBiz(ct, 1, AuthorizationRoles.BizAdmin); JobOperationsBiz jobopsbiz = new JobOperationsBiz(
List<JobOperationsLogInfoItem> log = await jobopsbiz.GetJobLogListAsync((Guid)jobid); ct,
var lastLog = log[log.Count - 1]; 1,
var lastLogJ = JObject.Parse(lastLog.StatusText); AuthorizationRoles.BizAdmin
var path = (string)lastLogJ["reportfilename"]; );
var FilePath = FileUtil.GetFullPathForTemporaryFile(path); List<JobOperationsLogInfoItem> log =
var FileName = FileUtil.StringToSafeFileName(await TranslationBiz.GetTranslationStaticAsync(ne.AyaType.ToString(), subTranslationId, ct) + $"-{ne.Name}.pdf").ToLowerInvariant(); await jobopsbiz.GetJobLogListAsync((Guid)jobid);
await m.SendEmailAsync(deliveryAddress, Subject, Body, ServerGlobalOpsSettingsCache.Notify, FilePath, FileName); var lastLog = log[log.Count - 1];
break; var lastLogJ = JObject.Parse(lastLog.StatusText);
} var path = (string)lastLogJ["reportfilename"];
var FilePath = FileUtil.GetFullPathForTemporaryFile(
path
);
var FileName = FileUtil
.StringToSafeFileName(
await TranslationBiz.GetTranslationStaticAsync(
ne.AyaType.ToString(),
subTranslationId,
ct
) + $"-{ne.Name}.pdf"
)
.ToLowerInvariant();
await m.SendEmailAsync(
deliveryAddress,
Subject,
Body,
ServerGlobalOpsSettingsCache.Notify,
FilePath,
FileName
);
break;
}
case JobStatus.Failed: case JobStatus.Failed:
case JobStatus.Absent: case JobStatus.Absent:
throw new ApplicationException($"REPORT RENDER JOB {jobid} started but failed"); throw new ApplicationException(
$"REPORT RENDER JOB {jobid} started but failed"
);
} }
} }
if (!done) if (!done)
throw new TimeoutException("JOB FAILED DUE TO REPORT RENDER TIMEOUT"); throw new TimeoutException(
"JOB FAILED DUE TO REPORT RENDER TIMEOUT"
);
} }
} }
else else
await m.SendEmailAsync(deliveryAddress, Subject, Body, ServerGlobalOpsSettingsCache.Notify); await m.SendEmailAsync(
deliveryAddress,
Subject,
Body,
ServerGlobalOpsSettingsCache.Notify
);
} }
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
await NotifyEventHelper.AddOpsProblemEvent("SMTP Customer Notification failed", ex); await NotifyEventHelper.AddOpsProblemEvent("SMTP Customer Notification failed", ex);
DeliveryLogItem.Fail = true; DeliveryLogItem.Fail = true;
DeliveryLogItem.Error = $"SMTP Notification failed to deliver for this Customer notify event: {ne}, message: {ex.Message}"; DeliveryLogItem.Error =
$"SMTP Notification failed to deliver for this Customer notify event: {ne}, message: {ex.Message}";
log.LogDebug(ex, $"DeliverSMTP Failure delivering Customer notify event: {ne}"); log.LogDebug(ex, $"DeliverSMTP Failure delivering Customer notify event: {ne}");
} }
finally finally
@@ -305,7 +435,11 @@ namespace AyaNova.Biz
private static string SetQuoteTokens(string TheField, Quote qt, string CustomerName) private static string SetQuoteTokens(string TheField, Quote qt, string CustomerName)
{ {
MatchCollection matches = Regex.Matches(TheField, @"\{{(.|\n)*?\}}", RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled); MatchCollection matches = Regex.Matches(
TheField,
@"\{{(.|\n)*?\}}",
RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled
);
//{{.*?}} //{{.*?}}
foreach (Match KeyMatch in matches) foreach (Match KeyMatch in matches)
{ {
@@ -329,17 +463,24 @@ namespace AyaNova.Biz
case "{{QuoteSerialNumber}}": case "{{QuoteSerialNumber}}":
TheField = TheField.Replace(KeyMatch.Value, qt.Serial.ToString()); TheField = TheField.Replace(KeyMatch.Value, qt.Serial.ToString());
break; break;
} }
} }
return TheField; return TheField;
} }
private static string SetWorkOrderTokens(
private static string SetWorkOrderTokens(string TheField, WorkOrder wo, string CustomerName, string statusName) string TheField,
WorkOrder wo,
string CustomerName,
string statusName
)
{ {
MatchCollection matches = Regex.Matches(TheField, @"\{{(.|\n)*?\}}", RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled); MatchCollection matches = Regex.Matches(
TheField,
@"\{{(.|\n)*?\}}",
RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled
);
//{{.*?}} //{{.*?}}
foreach (Match KeyMatch in matches) foreach (Match KeyMatch in matches)
{ {
@@ -363,17 +504,24 @@ namespace AyaNova.Biz
case "{{WorkOrderSerialNumber}}": case "{{WorkOrderSerialNumber}}":
TheField = TheField.Replace(KeyMatch.Value, wo.Serial.ToString()); TheField = TheField.Replace(KeyMatch.Value, wo.Serial.ToString());
break; break;
} }
} }
return TheField; return TheField;
} }
private static string SetCSRTokens(
private static string SetCSRTokens(string TheField, CustomerServiceRequest csr, string CustomerName, string requestedBy) string TheField,
CustomerServiceRequest csr,
string CustomerName,
string requestedBy
)
{ {
MatchCollection matches = Regex.Matches(TheField, @"\{{(.|\n)*?\}}", RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled); MatchCollection matches = Regex.Matches(
TheField,
@"\{{(.|\n)*?\}}",
RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled
);
//{{.*?}} //{{.*?}}
foreach (Match KeyMatch in matches) foreach (Match KeyMatch in matches)
{ {
@@ -407,9 +555,5 @@ namespace AyaNova.Biz
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
} //eoc
}//eoc } //eons
}//eons

View File

@@ -3,10 +3,8 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
namespace AyaNova.Util namespace AyaNova.Util
{ {
/// <summary> /// <summary>
/// Contains config values from bootup /// Contains config values from bootup
/// </summary> /// </summary>
@@ -14,23 +12,29 @@ namespace AyaNova.Util
{ {
//############################################################################################################ //############################################################################################################
//STATIC HARD CODED COMPILE TIME DEFAULTS NOT SET THROUGH CONFIG //STATIC HARD CODED COMPILE TIME DEFAULTS NOT SET THROUGH CONFIG
internal const int FAILED_AUTH_DELAY = 3000;//ms internal const int FAILED_AUTH_DELAY = 3000; //ms
internal const int JOB_OBJECT_HANDLE_BATCH_JOB_LOOP_DELAY = 200;//ms this delay is a temporary measure to ensure super big time consuming batch jobs don't use all server CPU resources internal const int JOB_OBJECT_HANDLE_BATCH_JOB_LOOP_DELAY = 200; //ms this delay is a temporary measure to ensure super big time consuming batch jobs don't use all server CPU resources
internal const int JOB_PROGRESS_UPDATE_AND_CANCEL_CHECK_SECONDS = 5;//seconds between progress updates and checks for cancellation of long running jobs internal const int JOB_PROGRESS_UPDATE_AND_CANCEL_CHECK_SECONDS = 5; //seconds between progress updates and checks for cancellation of long running jobs
internal const int JOB_OBJECT_EMAIL_LOOP_DELAY = 500;//ms this delay ensures multiple email sendings in a job don't overwhelm the mail server internal const int JOB_OBJECT_EMAIL_LOOP_DELAY = 500; //ms this delay ensures multiple email sendings in a job don't overwhelm the mail server
//UPLOAD LIMITS 1048576 = 1MiB for testing 10737420000 10737418240 10,737,418,240 //UPLOAD LIMITS 1048576 = 1MiB for testing 10737420000 10737418240 10,737,418,240
internal const long MAX_ATTACHMENT_UPLOAD_BYTES = 10737420000;//slight bit of overage as 10737418241=10GiB internal const long MAX_ATTACHMENT_UPLOAD_BYTES = 10737420000; //slight bit of overage as 10737418241=10GiB
internal const long MAX_LOGO_UPLOAD_BYTES = 512000;//500KiB limit internal const long MAX_LOGO_UPLOAD_BYTES = 512000; //500KiB limit
internal const long MAX_IMPORT_FILE_UPLOAD_BYTES = 104857600;//100MiB limit internal const long MAX_IMPORT_FILE_UPLOAD_BYTES = 104857600; //100MiB limit
internal const long MAX_REPORT_TEMPLATE_UPLOAD_BYTES = 15728640;//15MiB limit; currently the largest v7 export for a report template is 828kb, I'm guessing 15mb is more than enough internal const long MAX_REPORT_TEMPLATE_UPLOAD_BYTES = 15728640; //15MiB limit; currently the largest v7 export for a report template is 828kb, I'm guessing 15mb is more than enough
internal const long MAX_TRANSLATION_UPLOAD_BYTES = 15728640;//15MiB limit; currently export file is 200kb * 50 maximum at a time = 15mb internal const long MAX_TRANSLATION_UPLOAD_BYTES = 15728640; //15MiB limit; currently export file is 200kb * 50 maximum at a time = 15mb
//case 4632 safety constant as it's now referenced in multiple places
internal const string CUSTOMER_NOTIFICATION_ATTACHED_REPORT_RENDER_USERNAME =
"CUSTOMER NOTIFICATION - NO USER";
//############################################################################################################ //############################################################################################################
//############################ //############################
//SEEDING FLAG INTERNAL ONLY //SEEDING FLAG INTERNAL ONLY
//used to speed up seeding with bypasses to normal validation etc //used to speed up seeding with bypasses to normal validation etc
internal static bool SEEDING { get; set; } internal static bool SEEDING { get; set; }
//############################ //############################
//############################ //############################
@@ -42,17 +46,15 @@ namespace AyaNova.Util
//Diagnostic static values used during development, may not be related to config at all, this is just a convenient class to put them in //Diagnostic static values used during development, may not be related to config at all, this is just a convenient class to put them in
#if (DEBUG) #if (DEBUG)
internal static List<string> TranslationKeysRequested { get; set; } internal static List<string> TranslationKeysRequested { get; set; }
#endif #endif
//CONTENTROOTPATH //CONTENTROOTPATH
//** Not intended for end users but required in release mode //** Not intended for end users but required in release mode
internal static string AYANOVA_CONTENT_ROOT_PATH { get; set; } //Note: set in startup.cs, not in program.cs as it requires startup IHostingEnvironment internal static string AYANOVA_CONTENT_ROOT_PATH { get; set; } //Note: set in startup.cs, not in program.cs as it requires startup IHostingEnvironment
//LANGUAGE / Translation //LANGUAGE / Translation
internal static string AYANOVA_DEFAULT_TRANSLATION { get; set; } internal static string AYANOVA_DEFAULT_TRANSLATION { get; set; }
//** Not intended for end users //** Not intended for end users
internal static long AYANOVA_DEFAULT_TRANSLATION_ID { get; set; } //internal setting set at boot by TranslationBiz::ValidateTranslations internal static long AYANOVA_DEFAULT_TRANSLATION_ID { get; set; } //internal setting set at boot by TranslationBiz::ValidateTranslations
@@ -61,9 +63,9 @@ namespace AyaNova.Util
internal static string AYANOVA_USE_URLS { get; set; } internal static string AYANOVA_USE_URLS { get; set; }
internal static int AYANOVA_REPORT_RENDERING_TIMEOUT { get; set; } internal static int AYANOVA_REPORT_RENDERING_TIMEOUT { get; set; }
//DATABASE //DATABASE
internal static string AYANOVA_DB_CONNECTION { get; set; } internal static string AYANOVA_DB_CONNECTION { get; set; }
//** Not intended for end users //** Not intended for end users
internal static bool AYANOVA_PERMANENTLY_ERASE_DATABASE { get; set; } internal static bool AYANOVA_PERMANENTLY_ERASE_DATABASE { get; set; }
@@ -81,13 +83,13 @@ namespace AyaNova.Util
//REPORT RENDERING BROWSER PATH (if not set then will attempt to auto-download on first render) //REPORT RENDERING BROWSER PATH (if not set then will attempt to auto-download on first render)
internal static string AYANOVA_REPORT_RENDER_BROWSER_PATH { get; set; } internal static string AYANOVA_REPORT_RENDER_BROWSER_PATH { get; set; }
//REPORT RENDERING BROWSER PARAMS //REPORT RENDERING BROWSER PARAMS
internal static string AYANOVA_REPORT_RENDER_BROWSER_PARAMS { get; set; } internal static string AYANOVA_REPORT_RENDER_BROWSER_PARAMS { get; set; }
//REPORT RENDERING API URL OVERRIDE //REPORT RENDERING API URL OVERRIDE
internal static string AYANOVA_REPORT_RENDER_API_URL_OVERRIDE { get; set; } internal static string AYANOVA_REPORT_RENDER_API_URL_OVERRIDE { get; set; }
//LOGGING //LOGGING
internal static string AYANOVA_LOG_PATH { get; set; } internal static string AYANOVA_LOG_PATH { get; set; }
internal static string AYANOVA_LOG_LEVEL { get; set; } internal static string AYANOVA_LOG_LEVEL { get; set; }
@@ -97,9 +99,10 @@ namespace AyaNova.Util
internal static string AYANOVA_SET_SUPERUSER_PW { get; set; } internal static string AYANOVA_SET_SUPERUSER_PW { get; set; }
//HELPFUL INFORMATION FOR DIAGNOSTICS //HELPFUL INFORMATION FOR DIAGNOSTICS
internal static Dictionary<string, string> BOOT_DIAGNOSTIC_INFO { get; set; } = new Dictionary<string, string>(); internal static Dictionary<string, string> BOOT_DIAGNOSTIC_INFO { get; set; } =
internal static Dictionary<string, string> DBSERVER_DIAGNOSTIC_INFO { get; set; } = new Dictionary<string, string>(); new Dictionary<string, string>();
internal static Dictionary<string, string> DBSERVER_DIAGNOSTIC_INFO { get; set; } =
new Dictionary<string, string>();
/// <summary> /// <summary>
/// Populate the config from the configuration found at boot /// Populate the config from the configuration found at boot
@@ -108,7 +111,6 @@ namespace AyaNova.Util
/// <param name="config"></param> /// <param name="config"></param>
internal static void SetConfiguration(IConfigurationRoot config) internal static void SetConfiguration(IConfigurationRoot config)
{ {
#if (DEBUG) #if (DEBUG)
TranslationKeysRequested = new List<string>(); TranslationKeysRequested = new List<string>();
#endif #endif
@@ -121,7 +123,9 @@ namespace AyaNova.Util
//LANGUAGE //LANGUAGE
//TranslationBiz will validate this later at boot pfc and ensure a sane default is set (English) //TranslationBiz will validate this later at boot pfc and ensure a sane default is set (English)
AYANOVA_DEFAULT_TRANSLATION = config.GetValue<string>("AYANOVA_DEFAULT_TRANSLATION"); AYANOVA_DEFAULT_TRANSLATION = config.GetValue<string>("AYANOVA_DEFAULT_TRANSLATION");
AYANOVA_DEFAULT_TRANSLATION = string.IsNullOrWhiteSpace(AYANOVA_DEFAULT_TRANSLATION) ? "en" : AYANOVA_DEFAULT_TRANSLATION; AYANOVA_DEFAULT_TRANSLATION = string.IsNullOrWhiteSpace(AYANOVA_DEFAULT_TRANSLATION)
? "en"
: AYANOVA_DEFAULT_TRANSLATION;
string lowTranslation = AYANOVA_DEFAULT_TRANSLATION.ToLowerInvariant(); string lowTranslation = AYANOVA_DEFAULT_TRANSLATION.ToLowerInvariant();
switch (lowTranslation) switch (lowTranslation)
{ {
@@ -149,12 +153,11 @@ namespace AyaNova.Util
break; break;
} }
//LOGLEVEL //LOGLEVEL
AYANOVA_LOG_LEVEL = config.GetValue<string>("AYANOVA_LOG_LEVEL"); AYANOVA_LOG_LEVEL = config.GetValue<string>("AYANOVA_LOG_LEVEL");
AYANOVA_LOG_LEVEL = string.IsNullOrWhiteSpace(AYANOVA_LOG_LEVEL) ? "Info" : AYANOVA_LOG_LEVEL; AYANOVA_LOG_LEVEL = string.IsNullOrWhiteSpace(AYANOVA_LOG_LEVEL)
? "Info"
: AYANOVA_LOG_LEVEL;
//LOGGING DIAGNOSTIC LOG //LOGGING DIAGNOSTIC LOG
bTemp = config.GetValue<bool?>("AYANOVA_LOG_ENABLE_LOGGER_DIAGNOSTIC_LOG"); bTemp = config.GetValue<bool?>("AYANOVA_LOG_ENABLE_LOGGER_DIAGNOSTIC_LOG");
@@ -162,7 +165,9 @@ namespace AyaNova.Util
//PORT / API //PORT / API
AYANOVA_USE_URLS = config.GetValue<string>("AYANOVA_USE_URLS"); AYANOVA_USE_URLS = config.GetValue<string>("AYANOVA_USE_URLS");
AYANOVA_USE_URLS = string.IsNullOrWhiteSpace(AYANOVA_USE_URLS) ? "http://*:7575" : AYANOVA_USE_URLS; AYANOVA_USE_URLS = string.IsNullOrWhiteSpace(AYANOVA_USE_URLS)
? "http://*:7575"
: AYANOVA_USE_URLS;
AYANOVA_JWT_SECRET = config.GetValue<string>("AYANOVA_JWT_SECRET"); AYANOVA_JWT_SECRET = config.GetValue<string>("AYANOVA_JWT_SECRET");
@@ -172,20 +177,25 @@ namespace AyaNova.Util
//REPORT RENDERING //REPORT RENDERING
//RENDER OVERRIDE URL FOR CORS ISSUES BEHIND IIS (case 4398) //RENDER OVERRIDE URL FOR CORS ISSUES BEHIND IIS (case 4398)
AYANOVA_REPORT_RENDER_API_URL_OVERRIDE = config.GetValue<string>("AYANOVA_REPORT_RENDER_API_URL_OVERRIDE"); AYANOVA_REPORT_RENDER_API_URL_OVERRIDE = config.GetValue<string>(
"AYANOVA_REPORT_RENDER_API_URL_OVERRIDE"
);
//RENDER ENGINE PATH //RENDER ENGINE PATH
AYANOVA_REPORT_RENDER_BROWSER_PATH = ActualFullPath(config.GetValue<string>("AYANOVA_REPORT_RENDER_BROWSER_PATH")); AYANOVA_REPORT_RENDER_BROWSER_PATH = ActualFullPath(
config.GetValue<string>("AYANOVA_REPORT_RENDER_BROWSER_PATH")
);
//RENDER ENGINE PARAMS //RENDER ENGINE PARAMS
AYANOVA_REPORT_RENDER_BROWSER_PARAMS = config.GetValue<string>("AYANOVA_REPORT_RENDER_BROWSER_PARAMS"); AYANOVA_REPORT_RENDER_BROWSER_PARAMS = config.GetValue<string>(
"AYANOVA_REPORT_RENDER_BROWSER_PARAMS"
);
//PROCESS CONTROL //PROCESS CONTROL
int? nTemp = config.GetValue<int?>("AYANOVA_REPORT_RENDERING_TIMEOUT"); int? nTemp = config.GetValue<int?>("AYANOVA_REPORT_RENDERING_TIMEOUT");
AYANOVA_REPORT_RENDERING_TIMEOUT = (null == nTemp) ? 5 : (int)nTemp;//default AYANOVA_REPORT_RENDERING_TIMEOUT = (null == nTemp) ? 5 : (int)nTemp; //default
if (AYANOVA_REPORT_RENDERING_TIMEOUT < 1) AYANOVA_REPORT_RENDERING_TIMEOUT = 1; //one minute minimum timeout if (AYANOVA_REPORT_RENDERING_TIMEOUT < 1)
AYANOVA_REPORT_RENDERING_TIMEOUT = 1; //one minute minimum timeout
//DB //DB
AYANOVA_DB_CONNECTION = config.GetValue<string>("AYANOVA_DB_CONNECTION"); AYANOVA_DB_CONNECTION = config.GetValue<string>("AYANOVA_DB_CONNECTION");
@@ -197,41 +207,64 @@ namespace AyaNova.Util
bTemp = config.GetValue<bool?>("AYANOVA_REMOVE_LICENSE_FROM_DB"); bTemp = config.GetValue<bool?>("AYANOVA_REMOVE_LICENSE_FROM_DB");
AYANOVA_REMOVE_LICENSE_FROM_DB = (null == bTemp) ? false : (bool)bTemp; AYANOVA_REMOVE_LICENSE_FROM_DB = (null == bTemp) ? false : (bool)bTemp;
//FOLDERS //FOLDERS
string DataFolderPath = ActualFullPath(config.GetValue<string>("AYANOVA_DATA_PATH")); string DataFolderPath = ActualFullPath(config.GetValue<string>("AYANOVA_DATA_PATH"));
string LogPath = ActualFullPath(config.GetValue<string>("AYANOVA_LOG_PATH")); string LogPath = ActualFullPath(config.GetValue<string>("AYANOVA_LOG_PATH"));
string AttachmentFilesPath = ActualFullPath(config.GetValue<string>("AYANOVA_ATTACHMENT_FILES_PATH")); string AttachmentFilesPath = ActualFullPath(
string BackupFilesPath = ActualFullPath(config.GetValue<string>("AYANOVA_BACKUP_FILES_PATH")); config.GetValue<string>("AYANOVA_ATTACHMENT_FILES_PATH")
string TempFilesPath = ActualFullPath(config.GetValue<string>("AYANOVA_TEMP_FILES_PATH")); );
AYANOVA_BACKUP_PG_DUMP_PATH = ActualFullPath(config.GetValue<string>("AYANOVA_BACKUP_PG_DUMP_PATH")); string BackupFilesPath = ActualFullPath(
config.GetValue<string>("AYANOVA_BACKUP_FILES_PATH")
);
string TempFilesPath = ActualFullPath(
config.GetValue<string>("AYANOVA_TEMP_FILES_PATH")
);
AYANOVA_BACKUP_PG_DUMP_PATH = ActualFullPath(
config.GetValue<string>("AYANOVA_BACKUP_PG_DUMP_PATH")
);
if (string.IsNullOrWhiteSpace(DataFolderPath)) if (string.IsNullOrWhiteSpace(DataFolderPath))
{ {
//In this case *must* have paths for *everything* specified //In this case *must* have paths for *everything* specified
if (string.IsNullOrWhiteSpace(LogPath)) if (string.IsNullOrWhiteSpace(LogPath))
throw new System.ArgumentNullException("AYANOVA_LOG_PATH configuration setting missing and required"); throw new System.ArgumentNullException(
"AYANOVA_LOG_PATH configuration setting missing and required"
);
if (string.IsNullOrWhiteSpace(AttachmentFilesPath)) if (string.IsNullOrWhiteSpace(AttachmentFilesPath))
throw new System.ArgumentNullException("AYANOVA_ATTACHMENT_FILES_PATH configuration setting missing and required"); throw new System.ArgumentNullException(
"AYANOVA_ATTACHMENT_FILES_PATH configuration setting missing and required"
);
if (string.IsNullOrWhiteSpace(BackupFilesPath)) if (string.IsNullOrWhiteSpace(BackupFilesPath))
throw new System.ArgumentNullException("AYANOVA_BACKUP_FILES_PATH configuration setting missing and required"); throw new System.ArgumentNullException(
"AYANOVA_BACKUP_FILES_PATH configuration setting missing and required"
);
if (string.IsNullOrWhiteSpace(TempFilesPath)) if (string.IsNullOrWhiteSpace(TempFilesPath))
throw new System.ArgumentNullException("AYANOVA_TEMP_FILES_PATH configuration setting missing and required"); throw new System.ArgumentNullException(
"AYANOVA_TEMP_FILES_PATH configuration setting missing and required"
);
} }
//set paths //set paths
AYANOVA_LOG_PATH = (string.IsNullOrWhiteSpace(LogPath)) ? Path.Combine(DataFolderPath, "logs") : LogPath; AYANOVA_LOG_PATH =
AYANOVA_ATTACHMENT_FILES_PATH = (string.IsNullOrWhiteSpace(AttachmentFilesPath)) ? Path.Combine(DataFolderPath, "attachments") : AttachmentFilesPath; (string.IsNullOrWhiteSpace(LogPath))
AYANOVA_BACKUP_FILES_PATH = (string.IsNullOrWhiteSpace(BackupFilesPath)) ? Path.Combine(DataFolderPath, "backups") : BackupFilesPath; ? Path.Combine(DataFolderPath, "logs")
AYANOVA_TEMP_FILES_PATH = (string.IsNullOrWhiteSpace(TempFilesPath)) ? Path.Combine(DataFolderPath, "temp") : TempFilesPath; : LogPath;
AYANOVA_ATTACHMENT_FILES_PATH =
(string.IsNullOrWhiteSpace(AttachmentFilesPath))
? Path.Combine(DataFolderPath, "attachments")
: AttachmentFilesPath;
AYANOVA_BACKUP_FILES_PATH =
(string.IsNullOrWhiteSpace(BackupFilesPath))
? Path.Combine(DataFolderPath, "backups")
: BackupFilesPath;
AYANOVA_TEMP_FILES_PATH =
(string.IsNullOrWhiteSpace(TempFilesPath))
? Path.Combine(DataFolderPath, "temp")
: TempFilesPath;
#endregion server BASICS #endregion server BASICS
} }
internal static string ActualFullPath(string p) internal static string ActualFullPath(string p)
{ {
if (string.IsNullOrWhiteSpace(p)) if (string.IsNullOrWhiteSpace(p))
@@ -245,7 +278,9 @@ namespace AyaNova.Util
get get
{ {
if (string.IsNullOrWhiteSpace(AYANOVA_USE_URLS)) if (string.IsNullOrWhiteSpace(AYANOVA_USE_URLS))
{ return null; } {
return null;
}
if (!AYANOVA_USE_URLS.Contains(";")) if (!AYANOVA_USE_URLS.Contains(";"))
{ {
@@ -253,15 +288,7 @@ namespace AyaNova.Util
} }
var s = AYANOVA_USE_URLS.Split(';'); var s = AYANOVA_USE_URLS.Split(';');
return s[0].Replace("*", "localhost"); return s[0].Replace("*", "localhost");
} }
} }
} //eoc
} //eons
}//eoc
}//eons