From 75609bf2cec3231da83261b1e7ff3dc4a1759cfb Mon Sep 17 00:00:00 2001 From: Daniel Stolt Date: Sun, 16 Aug 2015 20:13:33 +0100 Subject: [PATCH 1/2] Excluded private locator URLs from JSON serialization in Azure Media Services. Fixes #4686. --- .../Models/Assets/EncoderMetadata/AssetFile.cs | 2 ++ .../Models/Assets/EncoderMetadata/Metadata.cs | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Orchard.Web/Modules/Orchard.Azure.MediaServices/Models/Assets/EncoderMetadata/AssetFile.cs b/src/Orchard.Web/Modules/Orchard.Azure.MediaServices/Models/Assets/EncoderMetadata/AssetFile.cs index 620619c26..aa35d2e7e 100644 --- a/src/Orchard.Web/Modules/Orchard.Azure.MediaServices/Models/Assets/EncoderMetadata/AssetFile.cs +++ b/src/Orchard.Web/Modules/Orchard.Azure.MediaServices/Models/Assets/EncoderMetadata/AssetFile.cs @@ -6,6 +6,7 @@ using System.Xml.Linq; using System.Xml.XPath; using Orchard.FileSystems.Media; using Orchard.Azure.MediaServices.Helpers; +using Newtonsoft.Json; namespace Orchard.Azure.MediaServices.Models.Assets.EncoderMetadata { public class AssetFile { @@ -120,6 +121,7 @@ namespace Orchard.Azure.MediaServices.Models.Assets.EncoderMetadata { /// /// A direct URL to download the asset file using a private locator. /// + [JsonIgnore] public string PrivateUrl { get { if (!String.IsNullOrEmpty(_parentMetadata.PrivateLocatorUrl)) { diff --git a/src/Orchard.Web/Modules/Orchard.Azure.MediaServices/Models/Assets/EncoderMetadata/Metadata.cs b/src/Orchard.Web/Modules/Orchard.Azure.MediaServices/Models/Assets/EncoderMetadata/Metadata.cs index 03d489ce3..6045fee5f 100644 --- a/src/Orchard.Web/Modules/Orchard.Azure.MediaServices/Models/Assets/EncoderMetadata/Metadata.cs +++ b/src/Orchard.Web/Modules/Orchard.Azure.MediaServices/Models/Assets/EncoderMetadata/Metadata.cs @@ -1,12 +1,11 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; -using System.Web; using System.Xml; using System.Xml.Linq; using System.Xml.XPath; using Orchard.FileSystems.Media; using Orchard.Azure.MediaServices.Helpers; +using Newtonsoft.Json; namespace Orchard.Azure.MediaServices.Models.Assets.EncoderMetadata { public class Metadata { @@ -46,6 +45,7 @@ namespace Orchard.Azure.MediaServices.Models.Assets.EncoderMetadata { } } + [JsonIgnore] public string PrivateLocatorUrl { get { return _privateLocatorUrl; From 7533cd887945e7a0241691bf819774faa9b457e4 Mon Sep 17 00:00:00 2001 From: Daniel Stolt Date: Mon, 17 Aug 2015 00:01:32 +0100 Subject: [PATCH 2/2] Improved DateTime localization and unit tests on Windows 10. Added support for era names to date formatting/parsing framework. Added handling of cultures with missing AM/PM designators. Fixes failing unit tests on Windows 10. Fixes #5595. --- .../Localization/DefaultDateFormatterTests.cs | 9 +- .../LocalizationDateTimeFormatProvider.cs | 31 ++++++ .../Services/CultureDateTimeFormatProvider.cs | 12 +++ .../Services/DefaultDateFormatter.cs | 95 +++++++++++-------- .../Services/IDateTimeFormatProvider.cs | 15 +++ 5 files changed, 121 insertions(+), 41 deletions(-) diff --git a/src/Orchard.Tests/Localization/DefaultDateFormatterTests.cs b/src/Orchard.Tests/Localization/DefaultDateFormatterTests.cs index 30d60ac07..5e59d8caa 100644 --- a/src/Orchard.Tests/Localization/DefaultDateFormatterTests.cs +++ b/src/Orchard.Tests/Localization/DefaultDateFormatterTests.cs @@ -87,9 +87,16 @@ namespace Orchard.Tests.Localization { var container = TestHelpers.InitializeContainer(culture.Name, "GregorianCalendar", TimeZoneInfo.Utc); var formats = container.Resolve(); var target = container.Resolve(); + var hoursToTest = new[] { 0, 6, 12, 18 }; + + // Fix for some cultures on Windows 10 where both designators for some reason + // are empty strings. A 24-hour time cannot possibly be round-tripped without any + // way to distinguish AM from PM, so for these cases test only 12-hour time. + if (culture.DateTimeFormat.AMDesignator == culture.DateTimeFormat.PMDesignator) + hoursToTest = new[] { 1, 6, 9, 12 }; foreach (var dateTimeFormat in formats.AllDateTimeFormats) { // All date and time formats supported by the culture. - foreach (var hour in new[] { 0, 6, 12, 18 }) { // Enough hours to cover all code paths (AM/PM, 12<->00, etc). + foreach (var hour in hoursToTest) { // Enough hours to cover all code paths (AM/PM, 12<->00, etc). DateTime dateTime = new DateTime(1998, 1, 1, hour, 30, 30, DateTimeKind.Utc); diff --git a/src/Orchard.Web/Modules/Orchard.Localization/Providers/LocalizationDateTimeFormatProvider.cs b/src/Orchard.Web/Modules/Orchard.Localization/Providers/LocalizationDateTimeFormatProvider.cs index 3cbca9c0a..734993ca0 100644 --- a/src/Orchard.Web/Modules/Orchard.Localization/Providers/LocalizationDateTimeFormatProvider.cs +++ b/src/Orchard.Web/Modules/Orchard.Localization/Providers/LocalizationDateTimeFormatProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using Orchard.Environment.Extensions; using Orchard.Localization.Services; @@ -172,5 +173,35 @@ namespace Orchard.Localization.Providers { return new string[] { "AM", "PM" }; } } + + public string GetEraName(int era) { + var t = T("A.D.;A.D.").Text; + var parts = t.Split(';'); + if (parts.Length >= era + 1) { + return parts[era]; + } + + return null; + } + + public string GetShortEraName(int era) { + var t = T("AD;AD").Text; + var parts = t.Split(';'); + if (parts.Length >= era + 1) { + return parts[era]; + } + + return null; + } + + public int GetEra(string eraName) { + var t = T("AD;AD").Text; + var parts = t.ToLowerInvariant().Split(';'); + if (parts.Contains(eraName.ToLowerInvariant())) { + return parts.ToList().IndexOf(eraName.ToLowerInvariant()); + } + + throw new ArgumentOutOfRangeException("eraName"); + } } } \ No newline at end of file diff --git a/src/Orchard/Localization/Services/CultureDateTimeFormatProvider.cs b/src/Orchard/Localization/Services/CultureDateTimeFormatProvider.cs index 1ffda8e2d..50f878600 100644 --- a/src/Orchard/Localization/Services/CultureDateTimeFormatProvider.cs +++ b/src/Orchard/Localization/Services/CultureDateTimeFormatProvider.cs @@ -178,6 +178,18 @@ namespace Orchard.Localization.Services { } } + public string GetEraName(int era) { + return DateTimeFormat.GetEraName(era); + } + + public string GetShortEraName(int era) { + return DateTimeFormat.GetAbbreviatedEraName(era); + } + + public int GetEra(string eraName) { + return DateTimeFormat.GetEra(eraName); + } + protected virtual DateTimeFormatInfo DateTimeFormat { get { var culture = CurrentCulture; diff --git a/src/Orchard/Localization/Services/DefaultDateFormatter.cs b/src/Orchard/Localization/Services/DefaultDateFormatter.cs index 696f1ab29..54aab947e 100644 --- a/src/Orchard/Localization/Services/DefaultDateFormatter.cs +++ b/src/Orchard/Localization/Services/DefaultDateFormatter.cs @@ -110,11 +110,11 @@ namespace Orchard.Localization.Services { int twoDigitYear, hour12, offsetHours, offsetMinutes; bool isPm; - string monthName, monthNameShort, monthNameGenitive, monthNameShortGenitive, dayName, dayNameShort, amPm, amPmShort, timeZone, offsetSign, fraction1Zero, fraction2Zero, fraction3Zero, fraction1Digit, fraction2Digit, fraction3Digit; - GetDateFormatValues(parts.Date, calendar, out twoDigitYear, out monthName, out monthNameShort, out monthNameGenitive, out monthNameShortGenitive, out dayName, out dayNameShort); + string monthName, monthNameShort, monthNameGenitive, monthNameShortGenitive, dayName, dayNameShort, eraName, eraNameShort, amPm, amPmShort, timeZone, offsetSign, fraction1Zero, fraction2Zero, fraction3Zero, fraction1Digit, fraction2Digit, fraction3Digit; + GetDateFormatValues(parts.Date, calendar, out twoDigitYear, out monthName, out monthNameShort, out monthNameGenitive, out monthNameShortGenitive, out dayName, out dayNameShort, out eraName, out eraNameShort); GetTimeFormatValues(parts.Time, out isPm, out hour12, out amPm, out amPmShort, out timeZone, out offsetSign, out offsetHours, out offsetMinutes, out fraction1Zero, out fraction2Zero, out fraction3Zero, out fraction1Digit, out fraction2Digit, out fraction3Digit); - return String.Format(formatString, parts.Date.Year, twoDigitYear, parts.Date.Month, monthName, monthNameShort, monthNameGenitive, monthNameShortGenitive, parts.Date.Day, dayName, dayNameShort, parts.Time.Hour, hour12, parts.Time.Minute, parts.Time.Second, fraction1Zero, fraction2Zero, fraction3Zero, fraction1Digit, fraction2Digit, fraction3Digit, amPm, amPmShort, timeZone, offsetSign, offsetHours, offsetMinutes); + return String.Format(formatString, parts.Date.Year, twoDigitYear, parts.Date.Month, monthName, monthNameShort, monthNameGenitive, monthNameShortGenitive, parts.Date.Day, dayName, dayNameShort, eraName, eraNameShort, parts.Time.Hour, hour12, parts.Time.Minute, parts.Time.Second, fraction1Zero, fraction2Zero, fraction3Zero, fraction1Digit, fraction2Digit, fraction3Digit, amPm, amPmShort, timeZone, offsetSign, offsetHours, offsetMinutes); } public virtual string FormatDate(DateParts parts) { @@ -129,10 +129,10 @@ namespace Orchard.Localization.Services { var calendar = CurrentCalendar; int twoDigitYear; - string monthName, monthNameShort, monthNameGenitive, monthNameShortGenitive, dayName, dayNameShort; - GetDateFormatValues(parts, calendar, out twoDigitYear, out monthName, out monthNameShort, out monthNameGenitive, out monthNameShortGenitive, out dayName, out dayNameShort); + string monthName, monthNameShort, monthNameGenitive, monthNameShortGenitive, dayName, dayNameShort, eraName, eraNameShort; + GetDateFormatValues(parts, calendar, out twoDigitYear, out monthName, out monthNameShort, out monthNameGenitive, out monthNameShortGenitive, out dayName, out dayNameShort, out eraName, out eraNameShort); - return String.Format(formatString, parts.Year, twoDigitYear, parts.Month, monthName, monthNameShort, monthNameGenitive, monthNameShortGenitive, parts.Day, dayName, dayNameShort); + return String.Format(formatString, parts.Year, twoDigitYear, parts.Month, monthName, monthNameShort, monthNameGenitive, monthNameShortGenitive, parts.Day, dayName, dayNameShort, eraName, eraNameShort); } public virtual string FormatTime(TimeParts parts) { @@ -148,7 +148,7 @@ namespace Orchard.Localization.Services { string amPm, amPmShort, timeZone, offsetSign, fraction1Zero, fraction2Zero, fraction3Zero, fraction1Digit, fraction2Digit, fraction3Digit; GetTimeFormatValues(parts, out isPm, out hour12, out amPm, out amPmShort, out timeZone, out offsetSign, out offsetHours, out offsetMinutes, out fraction1Zero, out fraction2Zero, out fraction3Zero, out fraction1Digit, out fraction2Digit, out fraction3Digit); - return String.Format(formatString, null, null, null, null, null, null, null, null, null, null, parts.Hour, hour12, parts.Minute, parts.Second, fraction1Zero, fraction2Zero, fraction3Zero, fraction1Digit, fraction2Digit, fraction3Digit, amPm, amPmShort, timeZone, offsetSign, offsetHours, offsetMinutes); + return String.Format(formatString, null, null, null, null, null, null, null, null, null, null, null, null, parts.Hour, hour12, parts.Minute, parts.Second, fraction1Zero, fraction2Zero, fraction3Zero, fraction1Digit, fraction2Digit, fraction3Digit, amPm, amPmShort, timeZone, offsetSign, offsetHours, offsetMinutes); } protected virtual DateTimeParts? TryParseDateTime(string dateTimeString, string format, IDictionary replacements) { @@ -238,8 +238,17 @@ namespace Orchard.Localization.Services { if (!m.Groups["amPm"].Success) { throw new FormatException("The string was not recognized as a valid time. The hour is in 12-hour notation but no AM/PM designator was found."); } - var isPm = m.Groups["amPm"].Value.Equals(_dateTimeFormatProvider.AmPmDesignators[1], StringComparison.InvariantCultureIgnoreCase); - hour = ConvertToHour24(Int32.Parse(m.Groups["hour12"].Value), isPm); + + var hour12 = Int32.Parse(m.Groups["hour12"].Value); + var isPm = false; + + // Fix for some cultures on Windows 10 where both designators for some reason are empty strings. + if (_dateTimeFormatProvider.AmPmDesignators[0] == _dateTimeFormatProvider.AmPmDesignators[1]) + isPm = hour12 >= 12; + else + isPm = m.Groups["amPm"].Value.Equals(_dateTimeFormatProvider.AmPmDesignators[1], StringComparison.InvariantCultureIgnoreCase); + + hour = ConvertToHour24(hour12, isPm); } if (m.Groups["minute"].Success) { @@ -290,7 +299,7 @@ namespace Orchard.Localization.Services { return new TimeParts(hour, minute, second, millisecond, kind, offset); } - protected virtual void GetDateFormatValues(DateParts parts, Calendar calendar, out int twoDigitYear, out string monthName, out string monthNameShort, out string monthNameGenitive, out string monthNameShortGenitive, out string dayName, out string dayNameShort) { + protected virtual void GetDateFormatValues(DateParts parts, Calendar calendar, out int twoDigitYear, out string monthName, out string monthNameShort, out string monthNameGenitive, out string monthNameShortGenitive, out string dayName, out string dayNameShort, out string eraName, out string eraNameShort) { var yearString = parts.Year.ToString("00", System.Globalization.CultureInfo.InvariantCulture); twoDigitYear = Int32.Parse(yearString.Substring(yearString.Length - 2)); monthName = parts.Month > 0 ? _dateTimeFormatProvider.MonthNames[parts.Month - 1] : null; @@ -299,6 +308,8 @@ namespace Orchard.Localization.Services { monthNameShortGenitive = parts.Month > 0 ? _dateTimeFormatProvider.MonthNamesShortGenitive[parts.Month - 1] : null; dayName = parts.Day > 0 ? _dateTimeFormatProvider.DayNames[(int)calendar.GetDayOfWeek(parts.ToDateTime(calendar))] : null; dayNameShort = parts.Day > 0 ? _dateTimeFormatProvider.DayNamesShort[(int)calendar.GetDayOfWeek(parts.ToDateTime(calendar))] : null; + eraName = parts.Day > 0 ? _dateTimeFormatProvider.GetEraName((int)calendar.GetEra(parts.ToDateTime(calendar))) : null; + eraNameShort = parts.Day > 0 ? _dateTimeFormatProvider.GetShortEraName((int)calendar.GetEra(parts.ToDateTime(calendar))) : null; } protected virtual void GetTimeFormatValues(TimeParts parts, out bool isPm, out int hour12, out string amPm, out string amPmShort, out string timeZone, out string offsetSign, out int offsetHours, out int offsetMinutes, out string fraction1Zero, out string fraction2Zero, out string fraction3Zero, out string fraction1Digit, out string fraction2Digit, out string fraction3Digit) { @@ -349,7 +360,9 @@ namespace Orchard.Localization.Services { {"yyyy", "(?[0-9]{4})"}, {"yyy", "(?[0-9]{3})"}, {"yy", "(?[0-9]{2})"}, - {"y", "(?[0-9]{1})"} + {"y", "(?[0-9]{1})"}, + {"gg", String.Format("(?{0})", String.Format("{0}|{1}", EscapeForRegex(_dateTimeFormatProvider.GetEraName(0)), EscapeForRegex(_dateTimeFormatProvider.GetEraName(1))))}, + {"g", String.Format("(?{0})", String.Format("{0}|{1}", EscapeForRegex(_dateTimeFormatProvider.GetEraName(0)), EscapeForRegex(_dateTimeFormatProvider.GetEraName(1))))} }; } @@ -404,40 +417,42 @@ namespace Orchard.Localization.Services { {"yyyy", "{0:0000}"}, {"yyy", "{0:000}"}, {"yy", "{1:00}"}, - {"y", "{1:0}"} + {"y", "{1:0}"}, + {"gg", "{10}"}, + {"g", "{10}"} }; } protected virtual Dictionary GetTimeFormatReplacements() { return new Dictionary() { - {"HH", "{10:00}"}, - {"H", "{10:#0}"}, - {"hh", "{11:00}"}, - {"h", "{11:#0}"}, - {"mm", "{12:00}"}, - {"m", "{12:#0}"}, - {"ss", "{13:00}"}, - {"s", "{13:#0}"}, - {"fffffff", "{16}0000"}, - {"ffffff", "{16}000"}, - {"fffff", "{16}00"}, - {"ffff", "{16}0"}, - {"fff", "{16}"}, - {"ff", "{15}"}, - {"f", "{14}"}, - {"FFFFFFF", "{19}"}, - {"FFFFFF", "{19}"}, - {"FFFFF", "{19}"}, - {"FFFF", "{19}"}, - {"FFF", "{19}"}, - {"FF", "{18}"}, - {"F", "{17}"}, - {"tt", "{20}"}, - {"t", "{21}"}, - {"K", "{22}"}, - {"zzz", "{23}{24:00}:{25:00}"}, - {"zz", "{23}{24:00}"}, - {"z", "{23}{24:#0}"} + {"HH", "{12:00}"}, + {"H", "{12:#0}"}, + {"hh", "{13:00}"}, + {"h", "{13:#0}"}, + {"mm", "{14:00}"}, + {"m", "{14:#0}"}, + {"ss", "{15:00}"}, + {"s", "{15:#0}"}, + {"fffffff", "{18}0000"}, + {"ffffff", "{18}000"}, + {"fffff", "{18}00"}, + {"ffff", "{18}0"}, + {"fff", "{18}"}, + {"ff", "{17}"}, + {"f", "{16}"}, + {"FFFFFFF", "{21}"}, + {"FFFFFF", "{21}"}, + {"FFFFF", "{21}"}, + {"FFFF", "{21}"}, + {"FFF", "{21}"}, + {"FF", "{20}"}, + {"F", "{19}"}, + {"tt", "{22}"}, + {"t", "{23}"}, + {"K", "{24}"}, + {"zzz", "{25}{26:00}:{27:00}"}, + {"zz", "{25}{26:00}"}, + {"z", "{25}{26:#0}"} }; } diff --git a/src/Orchard/Localization/Services/IDateTimeFormatProvider.cs b/src/Orchard/Localization/Services/IDateTimeFormatProvider.cs index c023cf974..839d293f4 100644 --- a/src/Orchard/Localization/Services/IDateTimeFormatProvider.cs +++ b/src/Orchard/Localization/Services/IDateTimeFormatProvider.cs @@ -162,5 +162,20 @@ namespace Orchard.Localization.Services { string[] AmPmDesignators { get; } + + /// + /// Returns a string containing the name of the specified era. + /// + string GetEraName(int era); + + /// + /// Returns a string containing the abbreviated name of the specified era, if an abbreviation exists. + /// + string GetShortEraName(int era); + + /// + /// Returns the integer representing the specified era. + /// + int GetEra(string eraName); } }