This repository has been archived on 2025-05-14. You can view files and clone it, but cannot push or open issues or pull requests.
Leigh Pointer 4f5b33d8df Null or empty check for FormatContent
Added null or empty check for the content and alias parameters at the beginning of the method.
2024-05-02 09:03:26 +02:00

628 lines
23 KiB
C#

using Oqtane.Models;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using File = Oqtane.Models.File;
namespace Oqtane.Shared
{
public static class Utilities
{
public static string ToModuleDefinitionName(this Type type)
{
if (type == null) return null;
var assemblyFullName = type.Assembly.FullName;
var assemblyName = assemblyFullName.Substring(0, assemblyFullName.IndexOf(",", StringComparison.Ordinal));
return $"{type.Namespace}, {assemblyName}";
}
public static (string UrlParameters, string Querystring, string Fragment) ParseParameters(string parameters)
{
// /urlparameters /urlparameters?Id=1 /urlparameters#5 /urlparameters?Id=1#5 /urlparameters?reload#5
// Id=1 Id=1#5 reload#5 reload
// #5
// create absolute url to convert to Uri
parameters = (!parameters.StartsWith("/") && !parameters.StartsWith("#") ? "?" : "") + parameters;
parameters = Constants.PackageRegistryUrl + parameters;
var uri = new Uri(parameters);
var querystring = uri.Query.Replace("?", "");
var fragment = uri.Fragment.Replace("#", "");
var urlparameters = uri.LocalPath;
urlparameters = (urlparameters == "/") ? "" : urlparameters;
return (urlparameters, querystring, fragment);
}
public static string NavigateUrl(string alias, string path, string parameters)
{
string querystring = "";
string fragment = "";
if (!string.IsNullOrEmpty(path) && !path.StartsWith("/")) path = "/" + path;
if (!string.IsNullOrEmpty(parameters))
{
(string urlparameters, querystring, fragment) = ParseParameters(parameters);
if (!string.IsNullOrEmpty(urlparameters))
{
path += (path.EndsWith("/") ? "" : "/") + $"{Constants.UrlParametersDelimiter}/{urlparameters.Substring(1)}";
}
}
// build url
var uriBuilder = new UriBuilder
{
Path = !string.IsNullOrEmpty(alias)
? (!string.IsNullOrEmpty(path)) ? $"{alias}{path}": $"{alias}"
: $"{path}",
Query = querystring,
Fragment = fragment
};
return uriBuilder.Uri.PathAndQuery;
}
public static string EditUrl(string alias, string path, int moduleid, string action, string parameters)
{
if (moduleid != -1)
{
path += $"/{Constants.ModuleDelimiter}/{moduleid}";
if (!string.IsNullOrEmpty(action))
{
path += $"/{action}";
}
}
return NavigateUrl(alias, path, parameters);
}
public static string FileUrl(Alias alias, string folderpath, string filename)
{
return FileUrl(alias, folderpath, filename, false);
}
public static string FileUrl(Alias alias, string folderpath, string filename, bool download)
{
var aliasUrl = (alias != null && !string.IsNullOrEmpty(alias.Path)) ? "/" + alias.Path : "";
var querystring = (download) ? "?download" : "";
return $"{alias?.BaseUrl}{aliasUrl}{Constants.FileUrl}{folderpath.Replace("\\", "/")}{filename}{querystring}";
}
public static string FileUrl(Alias alias, int fileid)
{
return FileUrl(alias, fileid, false);
}
public static string FileUrl(Alias alias, int fileid, bool download)
{
var aliasUrl = (alias != null && !string.IsNullOrEmpty(alias.Path)) ? "/" + alias.Path : "";
var querystring = (download) ? "?download" : "";
return $"{alias?.BaseUrl}{aliasUrl}{Constants.FileUrl}id/{fileid}{querystring}";
}
public static string ImageUrl(Alias alias, int fileId, int width, int height, string mode)
{
return ImageUrl(alias, fileId, width, height, mode, "", "", 0, false);
}
public static string ImageUrl(Alias alias, int fileId, int width, int height, string mode, string position, string background, int rotate, bool recreate)
{
var url = (alias != null && !string.IsNullOrEmpty(alias.Path)) ? "/" + alias.Path : "";
mode = string.IsNullOrEmpty(mode) ? "crop" : mode;
position = string.IsNullOrEmpty(position) ? "center" : position;
background = string.IsNullOrEmpty(background) ? "transparent" : background;
return $"{alias?.BaseUrl}{url}{Constants.ImageUrl}{fileId}/{width}/{height}/{mode}/{position}/{background}/{rotate}/{recreate}";
}
public static string TenantUrl(Alias alias, string url)
{
url = (!url.StartsWith("/")) ? "/" + url : url;
url = (alias != null && !string.IsNullOrEmpty(alias.Path)) ? "/" + alias.Path + url : url;
return $"{alias?.BaseUrl}{url}";
}
public static string AddUrlParameters(params object[] parameters)
{
var url = "";
for (var i = 0; i < parameters.Length; i++)
{
url += "/" + parameters[i].ToString();
}
return url;
}
public static string FormatContent(string content, Alias alias, string operation)
{
if (string.IsNullOrEmpty(content) || alias == null)
return content;
var aliasUrl = (alias != null && !string.IsNullOrEmpty(alias.Path)) ? "/" + alias.Path : "";
switch (operation)
{
case "save":
content = content.Replace(alias?.BaseUrl + aliasUrl + Constants.FileUrl, Constants.FileUrl);
// legacy
content = content.Replace(UrlCombine("Content", "Tenants", alias.TenantId.ToString(), "Sites", alias.SiteId.ToString()), "[siteroot]");
content = content.Replace(alias.Path + Constants.ContentUrl, Constants.ContentUrl);
break;
case "render":
content = content.Replace(Constants.FileUrl, alias?.BaseUrl + aliasUrl + Constants.FileUrl);
content = content.Replace("[wwwroot]", alias?.BaseUrl + aliasUrl + "/");
// legacy
content = content.Replace("[siteroot]", UrlCombine("Content", "Tenants", alias.TenantId.ToString(), "Sites", alias.SiteId.ToString()));
content = content.Replace(Constants.ContentUrl, alias.Path + Constants.ContentUrl);
break;
}
return content;
}
public static string GetTypeName(string fullyqualifiedtypename)
{
if (fullyqualifiedtypename.Contains(","))
{
return fullyqualifiedtypename.Substring(0, fullyqualifiedtypename.IndexOf(","));
}
else
{
return fullyqualifiedtypename;
}
}
public static string GetFullTypeName(string fullyqualifiedtypename)
{
if (fullyqualifiedtypename.Contains(", Version="))
{
return fullyqualifiedtypename.Substring(0, fullyqualifiedtypename.IndexOf(", Version="));
}
else
{
return fullyqualifiedtypename;
}
}
public static string GetAssemblyName(string fullyqualifiedtypename)
{
fullyqualifiedtypename = GetFullTypeName(fullyqualifiedtypename);
if (fullyqualifiedtypename.Contains(","))
{
return fullyqualifiedtypename.Substring(fullyqualifiedtypename.IndexOf(",") + 1).Trim();
}
else
{
return "";
}
}
public static string GetTypeNameLastSegment(string typename, int segment)
{
if (typename.Contains(","))
{
// remove assembly if fully qualified type
typename = typename.Substring(0, typename.IndexOf(","));
}
// segment 0 is the last segment, segment 1 is the second to last segment, etc...
string[] segments = typename.Split('.');
string name = "";
if (segment < segments.Length)
{
name = segments[segments.Length - (segment + 1)];
}
return name;
}
public static string GetFriendlyUrl(string url)
{
string result = "";
if (url != null)
{
var normalizedString = WebUtility.UrlDecode(url).ToLowerInvariant().Normalize(NormalizationForm.FormD);
var stringBuilder = new StringBuilder();
var stringLength = normalizedString.Length;
var prevdash = false;
char c;
for (int i = 0; i < stringLength; i++)
{
c = normalizedString[i];
switch (CharUnicodeInfo.GetUnicodeCategory(c))
{
case UnicodeCategory.LowercaseLetter:
case UnicodeCategory.UppercaseLetter:
case UnicodeCategory.DecimalDigitNumber:
if (c < 128)
stringBuilder.Append(c);
else
stringBuilder.Append(RemapInternationalCharToAscii(c));
prevdash = false;
break;
case UnicodeCategory.SpaceSeparator:
case UnicodeCategory.ConnectorPunctuation:
case UnicodeCategory.DashPunctuation:
case UnicodeCategory.OtherPunctuation:
case UnicodeCategory.MathSymbol:
if (!prevdash)
{
stringBuilder.Append('-');
prevdash = true;
}
break;
}
}
result = stringBuilder.ToString().Trim('-');
}
return result;
}
private static string RemapInternationalCharToAscii(char c)
{
string s = c.ToString().ToLowerInvariant();
if ("àåáâäãåą".Contains(s))
{
return "a";
}
else if ("èéêëę".Contains(s))
{
return "e";
}
else if ("ìíîïı".Contains(s))
{
return "i";
}
else if ("òóôõöøőð".Contains(s))
{
return "o";
}
else if ("ùúûüŭů".Contains(s))
{
return "u";
}
else if ("çćčĉ".Contains(s))
{
return "c";
}
else if ("żźž".Contains(s))
{
return "z";
}
else if ("śşšŝ".Contains(s))
{
return "s";
}
else if ("ñń".Contains(s))
{
return "n";
}
else if ("ýÿ".Contains(s))
{
return "y";
}
else if ("ğĝ".Contains(s))
{
return "g";
}
else if (c == 'ř')
{
return "r";
}
else if (c == 'ł')
{
return "l";
}
else if (c == 'đ')
{
return "d";
}
else if (c == 'ß')
{
return "ss";
}
else if (c == 'þ')
{
return "th";
}
else if (c == 'ĥ')
{
return "h";
}
else if (c == 'ĵ')
{
return "j";
}
else
{
return "";
}
}
public static bool IsValidEmail(string email)
{
if (string.IsNullOrEmpty(email)) return false;
return Regex.IsMatch(email,
@"^(?("")("".+?(?<!\\)""@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))" +
@"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-0-9a-z]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$",
RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250));
}
public static string PathCombine(params string[] segments)
{
var separators = new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };
for (int i = 1; i < segments.Length; i++)
{
if (Path.IsPathRooted(segments[i]))
{
segments[i] = segments[i].TrimStart(separators);
if (String.IsNullOrEmpty(segments[i]))
{
segments[i] = " ";
}
}
}
return Path.Combine(segments).TrimEnd();
}
public static string UrlCombine(params string[] segments)
{
var url = new List<string>();
for (int i = 0; i < segments.Length; i++)
{
segments[i] = segments[i].Replace("\\", "/");
if (!string.IsNullOrEmpty(segments[i]) && segments[i] != "/")
{
foreach (var segment in segments[i].Split('/', StringSplitOptions.RemoveEmptyEntries))
{
url.Add(segment);
}
}
}
return string.Join("/", url);
}
public static bool IsPathValid(this Folder folder)
{
return IsPathOrFileValid(folder.Name);
}
public static bool IsFileValid(this File file)
{
return IsPathOrFileValid(file.Name);
}
public static bool IsPathOrFileValid(this string name)
{
return (name != null &&
name.IndexOfAny(Constants.InvalidFileNameChars) == -1 &&
!Constants.InvalidFileNameEndingChars.Any(name.EndsWith) &&
!Constants.ReservedDevices.Split(',').Contains(name.ToUpper().Split('.')[0]));
}
public static bool TryGetQueryValue(
this Uri uri,
string key,
out string value,
string defaultValue = null)
{
value = defaultValue;
string query = uri.Query;
return !string.IsNullOrEmpty(query) && Utilities.ParseQueryString(query).TryGetValue(key, out value);
}
public static bool TryGetQueryValueInt(
this Uri uri,
string key,
out int value,
int defaultValue = 0)
{
value = defaultValue;
string s;
return uri.TryGetQueryValue(key, out s, (string)null) && int.TryParse(s, out value);
}
public static Dictionary<string, string> ParseQueryString(string query)
{
Dictionary<string, string> querystring = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); // case insensistive keys
if (!string.IsNullOrEmpty(query))
{
if (query.StartsWith("?"))
{
query = query.Substring(1); // ignore "?"
}
foreach (string kvp in query.Split('&', StringSplitOptions.RemoveEmptyEntries))
{
if (kvp != "")
{
if (kvp.Contains("="))
{
string[] pair = kvp.Split('=');
if (!querystring.ContainsKey(pair[0]))
{
querystring.Add(pair[0], pair[1]);
}
}
else
{
if (!querystring.ContainsKey(kvp))
{
querystring.Add(kvp, "true"); // default parameter when no value is provided
}
}
}
}
}
return querystring;
}
public static string CreateQueryString(Dictionary<string, string> parameters)
{
var querystring = "";
if (parameters.Count > 0)
{
foreach (var kvp in parameters)
{
querystring += (querystring == "") ? "?" : "&";
querystring += kvp.Key + "=" + kvp.Value;
}
}
return querystring;
}
public static string LogMessage(object @class, string message)
{
return $"[{@class.GetType()}] {message}";
}
public static DateTime? LocalDateAndTimeAsUtc(DateTime? date, string time, TimeZoneInfo localTimeZone = null)
{
if (date != null && !string.IsNullOrEmpty(time) && TimeSpan.TryParse(time, out TimeSpan timespan))
{
return LocalDateAndTimeAsUtc(date.Value.Date.Add(timespan), localTimeZone);
}
return null;
}
public static DateTime? LocalDateAndTimeAsUtc(DateTime? date, DateTime? time, TimeZoneInfo localTimeZone = null)
{
if (date != null)
{
if (time != null)
{
return LocalDateAndTimeAsUtc(date.Value.Date.Add(time.Value.TimeOfDay), localTimeZone);
}
return LocalDateAndTimeAsUtc(date.Value.Date, localTimeZone);
}
return null;
}
public static DateTime? LocalDateAndTimeAsUtc(DateTime? date, TimeZoneInfo localTimeZone = null)
{
if (date != null)
{
localTimeZone ??= TimeZoneInfo.Local;
return TimeZoneInfo.ConvertTime(date.Value, localTimeZone, TimeZoneInfo.Utc);
}
return null;
}
public static DateTime? UtcAsLocalDate(DateTime? dateTime, TimeZoneInfo timeZone = null)
{
return UtcAsLocalDateAndTime(dateTime, timeZone).date;
}
public static DateTime? UtcAsLocalDateTime(DateTime? dateTime, TimeZoneInfo timeZone = null)
{
var result = UtcAsLocalDateAndTime(dateTime, timeZone);
if (result.date != null && !string.IsNullOrEmpty(result.time) && TimeSpan.TryParse(result.time, out TimeSpan timespan))
{
result.date = result.date.Value.Add(timespan);
}
return result.date;
}
public static (DateTime? date, string time) UtcAsLocalDateAndTime(DateTime? dateTime, TimeZoneInfo timeZone = null)
{
timeZone ??= TimeZoneInfo.Local;
DateTime? localDateTime = null;
string localTime = string.Empty;
if (dateTime.HasValue && dateTime?.Kind != DateTimeKind.Local)
{
if (dateTime?.Kind == DateTimeKind.Unspecified)
{
// Treat Unspecified as Utc not Local. This is due to EF Core, on some databases, after retrieval will have DateTimeKind as Unspecified.
// All values in database should be UTC.
// Normal .net conversion treats Unspecified as local.
// https://docs.microsoft.com/en-us/dotnet/api/system.timezoneinfo.converttime?view=net-6.0
localDateTime = TimeZoneInfo.ConvertTime(new DateTime(dateTime.Value.Ticks, DateTimeKind.Utc), timeZone);
}
else
{
localDateTime = TimeZoneInfo.ConvertTime(dateTime.Value, timeZone);
}
}
if (localDateTime != null && localDateTime.Value.TimeOfDay.TotalSeconds != 0)
{
localTime = localDateTime.Value.ToString("HH:mm");
}
return (localDateTime?.Date, localTime);
}
public static bool IsPageModuleVisible(DateTime? effectiveDate, DateTime? expiryDate)
{
DateTime currentUtcTime = DateTime.UtcNow;
if (effectiveDate.HasValue && expiryDate.HasValue)
{
return currentUtcTime >= effectiveDate.Value && currentUtcTime <= expiryDate.Value;
}
else if (effectiveDate.HasValue)
{
return currentUtcTime >= effectiveDate.Value;
}
else if (expiryDate.HasValue)
{
// Include equality check here
return currentUtcTime <= expiryDate.Value;
}
else
{
return true;
}
}
public static bool ValidateEffectiveExpiryDates(DateTime? effectiveDate, DateTime? expiryDate)
{
// Treat DateTime.MinValue as null
effectiveDate ??= DateTime.MinValue;
expiryDate ??= DateTime.MinValue;
// Check if both effectiveDate and expiryDate have values
if (effectiveDate != DateTime.MinValue && expiryDate != DateTime.MinValue)
{
return effectiveDate <= expiryDate;
}
// Check if only effectiveDate has a value
else if (effectiveDate != DateTime.MinValue)
{
return true;
}
// Check if only expiryDate has a value
else if (expiryDate != DateTime.MinValue)
{
return true;
}
// If neither effectiveDate nor expiryDate has a value, consider the page/module visible
else
{
return true;
}
}
[Obsolete("ContentUrl(Alias alias, int fileId) is deprecated. Use FileUrl(Alias alias, int fileId) instead.", false)]
public static string ContentUrl(Alias alias, int fileId)
{
return ContentUrl(alias, fileId, false);
}
[Obsolete("ContentUrl(Alias alias, int fileId, bool asAttachment) is deprecated. Use FileUrl(Alias alias, int fileId, bool download) instead.", false)]
public static string ContentUrl(Alias alias, int fileId, bool asAttachment)
{
var aliasUrl = (alias != null && !string.IsNullOrEmpty(alias.Path)) ? "/" + alias.Path : "";
var method = asAttachment ? "/attach" : "";
return $"{alias?.BaseUrl}{aliasUrl}{Constants.ContentUrl}{fileId}{method}";
}
}
}