oqtane.framework/Oqtane.Shared/Shared/Utilities.cs

645 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
// id=1 id=1#5 reload#5 reload
// #5
// create absolute url to convert to Uri
parameters = (!parameters.StartsWith("/") && !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 IsEffectiveAndNotExpired(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)
{
return currentUtcTime <= expiryDate.Value;
}
else
{
return true;
}
}
public static bool ValidateEffectiveExpiryDates(DateTime? effectiveDate, DateTime? expiryDate)
{
effectiveDate ??= DateTime.MinValue;
expiryDate ??= DateTime.MinValue;
if (effectiveDate != DateTime.MinValue && expiryDate != DateTime.MinValue)
{
return effectiveDate <= expiryDate;
}
else if (effectiveDate != DateTime.MinValue)
{
return true;
}
else if (expiryDate != DateTime.MinValue)
{
return true;
}
else
{
return true;
}
}
public static string GenerateSimpleHash(string text)
{
unchecked // prevent overflow exception
{
int hash = 23;
foreach (char c in text)
{
hash = hash * 31 + c;
}
return hash.ToString("X8");
}
}
[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}";
}
[Obsolete("IsPageModuleVisible(DateTime?, DateTime?) is deprecated. Use IsEffectiveAndNotExpired(DateTime?, DateTime?) instead.", false)]
public static bool IsPageModuleVisible(DateTime? effectiveDate, DateTime? expiryDate)
{
return IsEffectiveAndNotExpired(effectiveDate, expiryDate);
}
}
}