Implementing support for user-configured default site calendar, plus related extensibility points (ICalendarSelector, ICalendarManager).

This commit is contained in:
Daniel Stolt
2013-12-07 00:50:02 +01:00
committed by Sebastien Ros
parent 60a7159f18
commit a306759748
18 changed files with 382 additions and 93 deletions

View File

@@ -66,6 +66,11 @@ namespace Orchard.Tests.Stubs {
set { throw new NotImplementedException(); }
}
public string SiteCalendar {
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
public ResourceDebugMode ResourceDebugMode {
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }

View File

@@ -18,16 +18,19 @@ namespace Orchard.Core.Settings.Drivers {
public class SiteSettingsPartDriver : ContentPartDriver<SiteSettingsPart> {
private readonly ISiteService _siteService;
private readonly ICultureManager _cultureManager;
private readonly ICalendarManager _calendarProvider;
private readonly IMembershipService _membershipService;
private readonly INotifier _notifier;
public SiteSettingsPartDriver(
ISiteService siteService,
ICultureManager cultureManager,
ICultureManager cultureManager,
ICalendarManager calendarProvider,
IMembershipService membershipService,
INotifier notifier) {
_siteService = siteService;
_cultureManager = cultureManager;
_calendarProvider = calendarProvider;
_membershipService = membershipService;
_notifier = notifier;
@@ -46,6 +49,7 @@ namespace Orchard.Core.Settings.Drivers {
var model = new SiteSettingsPartViewModel {
Site = site,
SiteCultures = _cultureManager.ListCultures(),
SiteCalendars = _calendarProvider.ListCalendars(),
TimeZones = TimeZoneInfo.GetSystemTimeZones()
};
@@ -58,7 +62,8 @@ namespace Orchard.Core.Settings.Drivers {
var model = new SiteSettingsPartViewModel {
Site = site,
SiteCultures = _cultureManager.ListCultures(),
TimeZones = TimeZoneInfo.GetSystemTimeZones()
SiteCalendars = _calendarProvider.ListCalendars(),
TimeZones = TimeZoneInfo.GetSystemTimeZones()
};
var previousBaseUrl = model.Site.BaseUrl;

View File

@@ -35,6 +35,11 @@ namespace Orchard.Core.Settings.Models {
set { this.Store(x => x.SiteCulture, value); }
}
public string SiteCalendar {
get { return this.Retrieve(x => x.SiteCalendar); }
set { this.Store(x => x.SiteCalendar, value); }
}
public ResourceDebugMode ResourceDebugMode {
get { return this.Retrieve(x => x.ResourceDebugMode); }
set { this.Store(x => x.ResourceDebugMode, value); }

View File

@@ -8,6 +8,7 @@ namespace Orchard.Core.Settings.ViewModels {
public class SiteSettingsPartViewModel {
public SiteSettingsPart Site { get; set; }
public IEnumerable<string> SiteCultures { get; set; }
public IEnumerable<string> SiteCalendars { get; set; }
public IEnumerable<TimeZoneInfo> TimeZones { get; set; }
[HiddenInput(DisplayValue = false)]
@@ -30,6 +31,11 @@ namespace Orchard.Core.Settings.ViewModels {
set { Site.SiteCulture = value; }
}
public string SiteCalendar {
get { return Site.SiteCalendar; }
set { Site.SiteCalendar = value; }
}
public string SuperUser {
get { return Site.SuperUser; }
set { Site.SuperUser = value; }

View File

@@ -15,22 +15,30 @@
@Html.ValidationMessage("SiteName", "*")
</div>
<div>
<label for="@Html.FieldIdFor(m => m.BaseUrl)">@T("Base url ")</label>
<label for="@Html.FieldIdFor(m => m.BaseUrl)">@T("Base URL")</label>
@Html.TextBoxFor(m => m.BaseUrl, new { @class = "text medium" })
<span class="hint">@T("Enter the fully qualified base url of your website.")</span>
<span class="hint">@T("Enter the fully qualified base URL of the web site.")</span>
<span class="hint">@T("e.g., http://localhost:30320/orchardlocal, http://www.yourdomain.com")</span>
</div>
<div>
<label for="SiteCulture">@T("Default Site Culture")</label>
@Html.DropDownList("SiteCulture", new SelectList(Model.SiteCultures, Model.SiteCulture))
@Html.ValidationMessage("SiteCulture", "*")
<p>@Html.ActionLink(T("Add or remove supported cultures for the site.").ToString(), "Culture")</p>
</div>
<div>
<label for="SiteCulture">@T("Default Site Culture")</label>
@Html.DropDownList("SiteCulture", new SelectList(Model.SiteCultures, Model.SiteCulture))
@Html.ValidationMessage("SiteCulture", "*")
<span class="hint">@T("Determines the default culture used to localize strings and to format and parse numbers, date and times.")</span>
<p>@Html.ActionLink(T("Add or remove supported cultures for the site").ToString(), "Culture")</p>
</div>
<div>
<label for="SiteCalendar">@T("Default Site Calendar")</label>
@Html.DropDownList("SiteCalendar", new[] { new SelectListItem { Text = T("Culture calendar").Text, Value = "" } }.Union(new SelectList(Model.SiteCalendars, Model.SiteCalendar)))
@Html.ValidationMessage("SiteCalendar", "*")
<span class="hint">@T("Determines the default calendar used when displaying and editing dates and times.")</span>
<span class="hint">@T("The 'Culture calendar' option means the default calendar for the culture of the current request will be used (not necessarily the configured default site culture).")</span>
</div>
<div>
<label for="TimeZone">@T("Default Time Zone")</label>
@Html.DropDownList("TimeZone", new[] { new SelectListItem { Text = T("Local to server").Text, Value = "" } }.Union(new SelectList(Model.TimeZones, "Id", "", Model.TimeZone)))
@Html.ValidationMessage("TimeZone", "*")
<span class="hint">@T("Determines the default time zone which will should be used to display date and times.")</span>
<span class="hint">@T("Determines the default time zone used when displaying and editing dates and times.")</span>
</div>
<div>
<label for="PageTitleSeparator">@T("Page title separator")</label>

View File

@@ -176,6 +176,11 @@ namespace Orchard.Setup {
set { throw new NotImplementedException(); }
}
public string SiteCalendar {
get { return ""; }
set { throw new NotImplementedException(); }
}
public ResourceDebugMode ResourceDebugMode {
get { return ResourceDebugMode.FromAppSetting; }
set { throw new NotImplementedException(); }

View File

@@ -70,6 +70,11 @@ namespace Orchard.Tokens.Tests {
set { throw new NotImplementedException(); }
}
public string SiteCalendar {
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
public ResourceDebugMode ResourceDebugMode {
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }

View File

@@ -1,4 +1,4 @@
@using Orchard.Core.Shapes.Localization
@using Orchard.Localization.Services
@using System.Globalization
@{

View File

@@ -1,4 +1,4 @@
@using Orchard.Core.Shapes.Localization
@using Orchard.Localization.Services
@using System.Globalization
@{

View File

@@ -0,0 +1,18 @@
using System;
namespace Orchard.Localization.Services {
public class CurrentCalendarWorkContext : IWorkContextStateProvider {
private readonly ICalendarManager _calendarManager;
public CurrentCalendarWorkContext(ICalendarManager calendarManager) {
_calendarManager = calendarManager;
}
public Func<WorkContext, T> Get<T>(string name) {
if (name == "CurrentCalendar") {
return ctx => (T)(object)_calendarManager.GetCurrentCalendar(ctx.HttpContext);
}
return null;
}
}
}

View File

@@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web;
using Orchard.Caching;
using Orchard.Data;
using Orchard.Localization.Records;
namespace Orchard.Localization.Services {
public class DefaultCalendarManager : ICalendarManager {
private readonly IEnumerable<ICalendarSelector> _calendarSelectors;
public DefaultCalendarManager(IEnumerable<ICalendarSelector> calendarSelectors) {
_calendarSelectors = calendarSelectors;
}
public IEnumerable<string> ListCalendars() {
// Return all the calendar implementations in System.Globalization.
// Could be done more dynamically using reflection, but that doesn't seem worth the performance hit.
return new[] {
"ChineseLunisolarCalendar",
"GregorianCalendar",
"HebrewCalendar",
"HijriCalendar",
"JapaneseCalendar",
"JapaneseLunisolarCalendar",
"JulianCalendar",
"KoreanCalendar",
"KoreanLunisolarCalendar",
"PersianCalendar",
"TaiwanCalendar",
"TaiwanLunisolarCalendar",
"ThaiBuddhistCalendar",
"UmAlQuraCalendar"
};
}
public string GetCurrentCalendar(HttpContextBase requestContext) {
var requestCalendar = _calendarSelectors
.Select(x => x.GetCalendar(requestContext))
.Where(x => x != null)
.OrderByDescending(x => x.Priority);
if (!requestCalendar.Any())
return String.Empty;
foreach (var calendar in requestCalendar) {
if (!String.IsNullOrEmpty(calendar.CalendarName)) {
return calendar.CalendarName;
}
}
return String.Empty;
}
public Calendar GetCalendarByName(string calendarName) {
switch (calendarName) {
case "ChineseLunisolarCalendar":
return new ChineseLunisolarCalendar();
case "GregorianCalendar":
return new GregorianCalendar();
case "HebrewCalendar":
return new HebrewCalendar();
case "HijriCalendar":
return new HijriCalendar();
case "JapaneseCalendar":
return new JapaneseCalendar();
case "JapaneseLunisolarCalendar":
return new JapaneseLunisolarCalendar();
case "JulianCalendar":
return new JulianCalendar();
case "KoreanCalendar":
return new KoreanCalendar();
case "KoreanLunisolarCalendar":
return new KoreanLunisolarCalendar();
case "PersianCalendar":
return new PersianCalendar();
case "TaiwanCalendar":
return new TaiwanCalendar();
case "TaiwanLunisolarCalendar":
return new TaiwanLunisolarCalendar();
case "ThaiBuddhistCalendar":
return new ThaiBuddhistCalendar();
case "UmAlQuraCalendar":
return new UmAlQuraCalendar();
default:
throw new ArgumentException(String.Format("The calendar name '{0}' is not a recognized System.Globalization calendar name.", calendarName), "calendarName");
}
}
}
}

View File

@@ -1,100 +1,173 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Orchard;
using Orchard.ContentManagement;
using Orchard.Settings;
namespace Orchard.Localization.Services {
public class DefaultDateServices : IDateServices {
public class DefaultDateServices : IDateServices {
private readonly IOrchardServices _orchardServices;
private readonly IDateTimeLocalization _dateTimeLocalization;
private readonly Lazy<CultureInfo> _cultureInfo;
private readonly IOrchardServices _orchardServices;
private readonly IDateTimeLocalization _dateTimeLocalization;
private readonly ISiteService _siteService;
private readonly ICalendarManager _calendarManager;
public DefaultDateServices(
IOrchardServices orchardServices,
IDateTimeLocalization dateTimeLocalization) {
public DefaultDateServices(
IOrchardServices orchardServices,
IDateTimeLocalization dateTimeLocalization,
ISiteService siteService,
ICalendarManager calendarManager) {
_orchardServices = orchardServices;
_dateTimeLocalization = dateTimeLocalization;
_cultureInfo = new Lazy<CultureInfo>(() => CultureInfo.GetCultureInfo(_orchardServices.WorkContext.CurrentCulture));
}
_orchardServices = orchardServices;
_dateTimeLocalization = dateTimeLocalization;
_siteService = siteService;
_calendarManager = calendarManager;
}
public virtual DateTime? ConvertToLocal(DateTime date) {
return ConvertToLocal(ToNullable(date));
}
public virtual DateTime? ConvertToLocal(DateTime date) {
return ConvertToLocal(ToNullable(date));
}
public virtual DateTime? ConvertToLocal(DateTime? date) {
if (!date.HasValue) {
return null;
}
return TimeZoneInfo.ConvertTimeFromUtc(date.Value, _orchardServices.WorkContext.CurrentTimeZone);
}
public virtual DateTime? ConvertToLocal(DateTime? date) {
if (!date.HasValue) {
return null;
}
return TimeZoneInfo.ConvertTimeFromUtc(date.Value, _orchardServices.WorkContext.CurrentTimeZone);
}
public virtual string ConvertToLocalString(DateTime date, string nullText = null) {
return ConvertToLocalString(ToNullable(date), _dateTimeLocalization.LongDateTimeFormat.Text, nullText);
}
public virtual string ConvertToLocalString(DateTime date, string nullText = null) {
return ConvertToLocalString(ToNullable(date), _dateTimeLocalization.LongDateTimeFormat.Text, nullText);
}
public virtual string ConvertToLocalString(DateTime date, string format, string nullText = null) {
return ConvertToLocalString(ToNullable(date), format, nullText);
}
public virtual string ConvertToLocalString(DateTime date, string format, string nullText = null) {
return ConvertToLocalString(ToNullable(date), format, nullText);
}
public virtual string ConvertToLocalString(DateTime? date, string format, string nullText = null) {
var localDate = ConvertToLocal(date);
if (!localDate.HasValue) {
return nullText;
}
return localDate.Value.ToString(format, _cultureInfo.Value);
}
public virtual string ConvertToLocalString(DateTime? date, string format, string nullText = null) {
var localDate = ConvertToLocal(date);
if (!localDate.HasValue) {
return nullText;
}
public virtual string ConvertToLocalDateString(DateTime date, string nullText = null) {
return ConvertToLocalDateString(ToNullable(date), nullText);
}
// If the configured current calendar is different from the default calendar
// of the configured current culture we must override the conversion process.
// We do this by using a custom CultureInfo modified to use GregorianCalendar
// (means no calendar conversion will be performed as part of the string
// formatting) and instead perform the calendar conversion ourselves.
public virtual string ConvertToLocalDateString(DateTime? date, string nullText = null) {
return ConvertToLocalString(date, _dateTimeLocalization.ShortDateFormat.Text, nullText);
}
var cultureInfo = CurrentCulture;
var usingCultureCalendar = CurrentCulture.DateTimeFormat.Calendar.GetType().IsInstanceOfType(CurrentCalendar);
if (!usingCultureCalendar) {
cultureInfo = (CultureInfo)CurrentCulture.Clone();
cultureInfo.DateTimeFormat.Calendar = new GregorianCalendar();
var calendar = CurrentCalendar;
localDate = new DateTime(calendar.GetYear(localDate.Value), calendar.GetMonth(localDate.Value), calendar.GetDayOfMonth(localDate.Value), calendar.GetHour(localDate.Value), calendar.GetMinute(localDate.Value), calendar.GetSecond(localDate.Value));
}
public virtual string ConvertToLocalTimeString(DateTime date, string nullText = null) {
return ConvertToLocalTimeString(ToNullable(date), nullText);
}
return localDate.Value.ToString(format, cultureInfo);
}
public virtual string ConvertToLocalTimeString(DateTime? date, string nullText = null) {
return ConvertToLocalString(date, _dateTimeLocalization.ShortTimeFormat.Text, nullText);
}
public virtual string ConvertToLocalDateString(DateTime date, string nullText = null) {
return ConvertToLocalDateString(ToNullable(date), nullText);
}
public virtual DateTime? ConvertFromLocal(DateTime date) {
return ConvertToLocal(ToNullable(date));
}
public virtual string ConvertToLocalDateString(DateTime? date, string nullText = null) {
return ConvertToLocalString(date, _dateTimeLocalization.ShortDateFormat.Text, nullText);
}
public virtual DateTime? ConvertFromLocal(DateTime? date) {
if (!date.HasValue) {
return null;
}
return TimeZoneInfo.ConvertTimeToUtc(date.Value, _orchardServices.WorkContext.CurrentTimeZone);
}
public virtual string ConvertToLocalTimeString(DateTime date, string nullText = null) {
return ConvertToLocalTimeString(ToNullable(date), nullText);
}
public virtual DateTime? ConvertFromLocalString(string dateString) {
if (String.IsNullOrWhiteSpace(dateString)) {
return null;
}
var localDate = DateTime.Parse(dateString, _cultureInfo.Value);
return ConvertFromLocal(localDate);
}
public virtual string ConvertToLocalTimeString(DateTime? date, string nullText = null) {
return ConvertToLocalString(date, _dateTimeLocalization.ShortTimeFormat.Text, nullText);
}
public virtual DateTime? ConvertFromLocalString(string dateString, string timeString) {
if (String.IsNullOrWhiteSpace(dateString) && String.IsNullOrWhiteSpace(timeString)) {
return null;
}
var localDate = !String.IsNullOrWhiteSpace(dateString) ? DateTime.Parse(dateString, _cultureInfo.Value) : new DateTime(1980, 1, 1);
var localTime = !String.IsNullOrWhiteSpace(timeString) ? DateTime.Parse(timeString, _cultureInfo.Value) : new DateTime(1980, 1, 1, 12, 0, 0);
var localDateTime = new DateTime(localDate.Year, localDate.Month, localDate.Day, localTime.Hour, localTime.Minute, localTime.Second);
return ConvertFromLocal(localDateTime);
}
public virtual DateTime? ConvertFromLocal(DateTime date) {
return ConvertToLocal(ToNullable(date));
}
protected virtual DateTime? ToNullable(DateTime date) {
return date == DateTime.MinValue ? new DateTime?() : new DateTime?(date);
}
}
public virtual DateTime? ConvertFromLocal(DateTime? date) {
if (!date.HasValue) {
return null;
}
return TimeZoneInfo.ConvertTimeToUtc(date.Value, _orchardServices.WorkContext.CurrentTimeZone);
}
public virtual DateTime? ConvertFromLocalString(string dateString) {
if (String.IsNullOrWhiteSpace(dateString)) {
return null;
}
// If the configured current calendar is different from the default calendar
// of the configured current culture we must override the conversion process.
// We do this by using a custom CultureInfo modified to use GregorianCalendar
// (means no calendar conversion will be performed as part of the string
// parsing) and instead perform the calendar conversion ourselves.
var cultureInfo = CurrentCulture;
var usingCultureCalendar = CurrentCulture.DateTimeFormat.Calendar.GetType().IsInstanceOfType(CurrentCalendar);
if (!usingCultureCalendar) {
cultureInfo = (CultureInfo)CurrentCulture.Clone();
cultureInfo.DateTimeFormat.Calendar = new GregorianCalendar();
}
var localDate = DateTime.Parse(dateString, CurrentCulture);
if (!usingCultureCalendar) {
var calendar = CurrentCalendar;
localDate = calendar.ToDateTime(localDate.Year, localDate.Month, localDate.Day, localDate.Hour, localDate.Minute, localDate.Second, localDate.Millisecond);
}
return ConvertFromLocal(localDate);
}
public virtual DateTime? ConvertFromLocalString(string dateString, string timeString) {
if (String.IsNullOrWhiteSpace(dateString) && String.IsNullOrWhiteSpace(timeString)) {
return null;
}
// If the configured current calendar is different from the default calendar
// of the configured current culture we must override the conversion process.
// We do this by using a custom CultureInfo modified to use GregorianCalendar
// (means no calendar conversion will be performed as part of the string
// parsing) and instead perform the calendar conversion ourselves.
var cultureInfo = CurrentCulture;
var usingCultureCalendar = CurrentCulture.DateTimeFormat.Calendar.GetType().IsInstanceOfType(CurrentCalendar);
if (!usingCultureCalendar) {
cultureInfo = (CultureInfo)CurrentCulture.Clone();
cultureInfo.DateTimeFormat.Calendar = new GregorianCalendar();
}
var localDate = !String.IsNullOrWhiteSpace(dateString) ? DateTime.Parse(dateString, cultureInfo) : new DateTime(1980, 1, 1);
var localTime = !String.IsNullOrWhiteSpace(timeString) ? DateTime.Parse(timeString, cultureInfo) : new DateTime(1980, 1, 1, 12, 0, 0);
var localDateTime = new DateTime(localDate.Year, localDate.Month, localDate.Day, localTime.Hour, localTime.Minute, localTime.Second);
if (!usingCultureCalendar) {
var calendar = CurrentCalendar;
localDateTime = calendar.ToDateTime(localDateTime.Year, localDateTime.Month, localDateTime.Day, localDateTime.Hour, localDateTime.Minute, localDateTime.Second, localDateTime.Millisecond);
}
return ConvertFromLocal(localDateTime);
}
protected virtual CultureInfo CurrentCulture {
get {
return CultureInfo.GetCultureInfo(_orchardServices.WorkContext.CurrentCulture);
}
}
protected virtual Calendar CurrentCalendar {
get {
if (!String.IsNullOrEmpty(_orchardServices.WorkContext.CurrentCalendar))
return _calendarManager.GetCalendarByName(_orchardServices.WorkContext.CurrentCalendar);
return CurrentCulture.Calendar;
}
}
protected virtual DateTime? ToNullable(DateTime date) {
return date == DateTime.MinValue ? new DateTime?() : new DateTime?(date);
}
}
}

View File

@@ -0,0 +1,11 @@
using System.Collections.Generic;
using System.Globalization;
using System.Web;
namespace Orchard.Localization.Services {
public interface ICalendarManager : IDependency {
IEnumerable<string> ListCalendars();
string GetCurrentCalendar(HttpContextBase requestContext);
Calendar GetCalendarByName(string calendarName);
}
}

View File

@@ -0,0 +1,18 @@
using System.Web;
namespace Orchard.Localization.Services {
public class CalendarSelectorResult {
public int Priority {
get;
set;
}
public string CalendarName {
get;
set;
}
}
public interface ICalendarSelector : IDependency {
CalendarSelectorResult GetCalendar(HttpContextBase context);
}
}

View File

@@ -0,0 +1,25 @@
using System;
using System.Web;
namespace Orchard.Localization.Services {
public class SiteCalendarSelector : ICalendarSelector {
private readonly IWorkContextAccessor _workContextAccessor;
public SiteCalendarSelector(IWorkContextAccessor workContextAccessor) {
_workContextAccessor = workContextAccessor;
}
public CalendarSelectorResult GetCalendar(HttpContextBase context) {
string currentCalendarName = _workContextAccessor.GetContext().CurrentSite.SiteCalendar;
if (String.IsNullOrEmpty(currentCalendarName)) {
return null;
}
return new CalendarSelectorResult {
Priority = -5,
CalendarName = currentCalendarName
};
}
}
}

View File

@@ -266,13 +266,18 @@
<Compile Include="FileSystems\Media\FileSystemStorageProvider.cs" />
<Compile Include="FileSystems\Media\IMimeTypeProvider.cs" />
<Compile Include="Indexing\ISearchBits.cs" />
<Compile Include="Localization\Services\CurrentCalendarWorkContext.cs" />
<Compile Include="Localization\Services\CurrentCultureWorkContext.cs" />
<Compile Include="Localization\Services\DefaultCalendarManager.cs" />
<Compile Include="Localization\Services\DefaultDateTimeLocalization.cs" />
<Compile Include="Localization\Services\DefaultDateServices.cs" />
<Compile Include="Localization\Services\DefaultLocalizedStringManager.cs" />
<Compile Include="Localization\Services\ICalendarSelector.cs" />
<Compile Include="Localization\Services\ICalendarManager.cs" />
<Compile Include="Localization\Services\IDateServices.cs" />
<Compile Include="Localization\Services\IDateTimeLocalization.cs" />
<Compile Include="Localization\Services\ILocalizedStringManager.cs" />
<Compile Include="Localization\Services\SiteCalendarSelector.cs" />
<Compile Include="Logging\OrchardFileAppender.cs" />
<Compile Include="Logging\OrchardLog4netFactory.cs" />
<Compile Include="Logging\OrchardLog4netLogger.cs" />

View File

@@ -11,6 +11,7 @@ namespace Orchard.Settings {
string SuperUser { get; }
string HomePage { get; set; }
string SiteCulture { get; set; }
string SiteCalendar { get; set; }
ResourceDebugMode ResourceDebugMode { get; set; }
int PageSize { get; set; }
string BaseUrl { get; }

View File

@@ -24,11 +24,9 @@ namespace Orchard {
/// <returns>True if the dependency could be resolved, false otherwise</returns>
public abstract bool TryResolve<T>(out T service);
public abstract T GetState<T>(string name);
public abstract T GetState<T>(string name);
public abstract void SetState<T>(string name, T value);
/// <summary>
/// The http context corresponding to the work context
/// </summary>
@@ -77,6 +75,14 @@ namespace Orchard {
set { SetState("CurrentCulture", value); }
}
/// <summary>
/// Active calendar of the work context
/// </summary>
public string CurrentCalendar {
get { return GetState<string>("CurrentCalendar"); }
set { SetState("CurrentCalendar", value); }
}
/// <summary>
/// Time zone of the work context
/// </summary>