Namespace: ITHit.WebDAV.Server.CalDav
The ICalendarFolder type exposes the following members.
Name | Description | |
---|---|---|
CalendarDescription |
Gets a human-readable description of the calendar collection.
| |
Created |
Gets the creation date of the item in repository expressed as the coordinated universal time (UTC).
(Inherited from IHierarchyItemBase.) | |
MaxAttendeesPerInstance |
Provides a numeric value indicating the maximum number of
ATTENDEE properties in any instance of a calendar object resource
stored in a calendar collection.
| |
MaxInstances |
Gets a numeric value indicating the maximum number of
recurrence instances that a calendar object resource stored in a
calendar collection can generate.
| |
MaxResourceSize |
Gets a numeric value indicating the maximum size of a
resource in bytes that the server is willing to accept when a
calendar object resource is stored in a calendar collection.
| |
Modified |
Gets the last modification date of the item in repository expressed as the coordinated universal time (UTC).
(Inherited from IHierarchyItemBase.) | |
Name |
Gets the name of the item in repository.
(Inherited from IHierarchyItemBase.) | |
Path |
Unique item path in the repository relative to storage root.
(Inherited from IHierarchyItemBase.) | |
SupportedComponentTypes |
Gets a calendar component types (e.g., VEVENT, VTODO, etc.)
that calendar object resources can contain in the calendar collection.
| |
UtcMaxDateTime |
Gets a DATE-TIME value indicating the latest date and
time (in UTC) that the server is willing to accept for any DATE or
DATE-TIME value in a calendar object resource stored in a calendar
collection.
| |
UtcMinDateTime |
Gets a DATE-TIME value indicating the earliest date and
time (in UTC) that the server is willing to accept for any DATE or
DATE-TIME value in a calendar object resource stored in a calendar
collection.
|
Name | Description | |
---|---|---|
CopyToAsync |
Creates a copy of this item with a new name in the destination folder.
(Inherited from IHierarchyItem.) | |
CreateFileAsync |
Creates new WebDAV file with the specified name in this folder.
(Inherited from IFolder.) | |
CreateFolderAsync |
Creates new WebDAV folder with the specified name in this folder.
(Inherited from IFolder.) | |
DeleteAsync |
Deletes this item.
(Inherited from IHierarchyItem.) | |
GetChildrenAsync |
Gets direct children of this folder.
(Inherited from IItemCollection.) | |
GetPropertiesAsync |
Gets values of all properties or selected properties for this item.
(Inherited from IHierarchyItem.) | |
GetPropertyNamesAsync |
Gets names of all properties for this item.
(Inherited from IHierarchyItem.) | |
MoveToAsync |
Moves this item to the destination folder under a new name.
(Inherited from IHierarchyItem.) | |
MultiGetAsync |
Gets a list of calendar files that correspont to the specified list of item paths.
(Inherited from ICalendarReport.) | |
QueryAsync |
Gets a list of calendar files that match specified filter.
(Inherited from ICalendarReport.) | |
UpdatePropertiesAsync |
Adds, modifies and removes properties for this item.
(Inherited from IHierarchyItem.) |
The code below is part of 'CalDAVServer.SqlStorage.AspNet' C# & VB samples provided with the SDK.
// Note: // - Mozilla Thunderbird Lightning requires ICurrentUserPrincipal on calendar folder, it does not support discovery. // - Outlook CalDAV Synchronizer requires IAclHierarchyItem on calendar folder. public class CalendarFolder : DavHierarchyItem, ICalendarFolder, IAppleCalendar, ICurrentUserPrincipal, IAclHierarchyItem { public static async Task<ICalendarFolder> LoadByIdAsync(DavContext context, Guid calendarFolderId) { // Load only calendar that the use has access to. // Also load complete ACL for this calendar. string sql = @"SELECT * FROM [cal_CalendarFolder] WHERE [CalendarFolderId] = @CalendarFolderId AND [CalendarFolderId] IN (SELECT [CalendarFolderId] FROM [cal_Access] WHERE [UserId]=@UserId) ; SELECT * FROM [cal_Access] WHERE [CalendarFolderId] = @CalendarFolderId AND [CalendarFolderId] IN (SELECT [CalendarFolderId] FROM [cal_Access] WHERE [UserId]=@UserId)"; return (await LoadAsync(context, sql, "@UserId", context.UserId , "@CalendarFolderId", calendarFolderId )).FirstOrDefault(); } public static async Task<IEnumerable<ICalendarFolder>> LoadAllAsync(DavContext context) { // Load only calendars that the use has access to. // Also load complete ACL for each calendar, but only if user has access to that calendar. string sql = @"SELECT * FROM [cal_CalendarFolder] WHERE [CalendarFolderId] IN (SELECT [CalendarFolderId] FROM [cal_Access] WHERE [UserId]=@UserId) ; SELECT * FROM [cal_Access] WHERE [CalendarFolderId] IN (SELECT [CalendarFolderId] FROM [cal_Access] WHERE [UserId]=@UserId)"; return await LoadAsync(context, sql, "@UserId", context.UserId); } private static async Task<IEnumerable<ICalendarFolder>> LoadAsync(DavContext context, string sql, params object[] prms) { IList<ICalendarFolder> calendarFolders = new List<ICalendarFolder>(); using (SqlDataReader reader = await context.ExecuteReaderAsync(sql, prms)) { DataTable calendars = new DataTable(); calendars.Load(reader); DataTable access = new DataTable(); access.Load(reader); foreach (DataRow rowCalendarFolder in calendars.Rows) { Guid calendarFolderId = rowCalendarFolder.Field<Guid>("CalendarFolderId"); string filter = string.Format("CalendarFolderId = '{0}'", calendarFolderId); DataRow[] rowsAccess = access.Select(filter); calendarFolders.Add(new CalendarFolder(context, calendarFolderId, rowCalendarFolder, rowsAccess)); } } return calendarFolders; } public static async Task<ICalendarFolder> CreateCalendarFolderAsync(DavContext context, string name, string description) { // 1. Create calendar. // 2. Grant owner privileges to the user on the created calendar. string sql = @"INSERT INTO [cal_CalendarFolder] ( [CalendarFolderId] , [Name] , [Description] ) VALUES ( @CalendarFolderId , @Name , @Description ) ; INSERT INTO [cal_Access] ( [CalendarFolderId] , [UserId] , [Owner] , [Read] , [Write] ) VALUES ( @CalendarFolderId , @UserId , @Owner , @Read , @Write )"; Guid calendarFolderId = Guid.NewGuid(); await context.ExecuteNonQueryAsync(sql, "@CalendarFolderId" , calendarFolderId , "@Name" , name , "@Description" , description , "@UserId" , context.UserId , "@Owner" , true , "@Read" , true , "@Write" , true ); return await LoadByIdAsync(context, calendarFolderId); } private readonly Guid calendarFolderId; private readonly DataRow rowCalendarFolder; private readonly DataRow[] rowsAccess; public override string Name { get { return rowCalendarFolder != null ? rowCalendarFolder.Field<string>("Name") : null; } } public override string Path { get { return string.Format("{0}{1}/", CalendarsRootFolder.CalendarsRootFolderPath, calendarFolderId); } } private CalendarFolder(DavContext context, Guid calendarFolderId, DataRow calendar, DataRow[] rowsAccess) : base(context) { this.calendarFolderId = calendarFolderId; this.rowCalendarFolder = calendar; this.rowsAccess = rowsAccess; } public async Task<IEnumerable<ICalendarFile>> MultiGetAsync(IEnumerable<string> pathList, IEnumerable<PropertyName> propNames) { // Get list of UIDs from path list. IEnumerable<string> uids = pathList.Select(a => System.IO.Path.GetFileNameWithoutExtension(a)); return await CalendarFile.LoadByUidsAsync(Context, uids, PropsToLoad.All); } public async Task<IEnumerable<ICalendarFile>> QueryAsync(string rawQuery, IEnumerable<PropertyName> propNames) { // For the sake of simplicity we just call GetChildren returning all items. // Typically you will return only items that match the query. return (await GetChildrenAsync(propNames.ToList(), null, null, null)).Page.Cast<ICalendarFile>(); } public IEnumerable<CalendarComponentType> SupportedComponentTypes { get { return new[] { CalendarComponentType.VEVENT, CalendarComponentType.VTODO, }; } } public string CalendarDescription { get { return rowCalendarFolder.Field<string>("Description"); } } public ulong MaxResourceSize { get { return ulong.MaxValue; } } public ulong MaxInstances { get { return ulong.MaxValue; } } public ulong MaxAttendeesPerInstance { get { return ulong.MaxValue; } } public DateTime UtcMinDateTime { get { return DateTime.MinValue.ToUniversalTime(); } } public DateTime UtcMaxDateTime { get { return DateTime.MaxValue.ToUniversalTime(); } } public async Task<PageResults> GetChildrenAsync(IList<PropertyName> propNames, long? offset, long? nResults, IList<OrderProperty> orderProps) { // Here we enumerate all events and to-dos contained in this calendar. // You can filter children items in this implementation and // return only items that you want to be available for this // particular user. // Typically only getcontenttype and getetag properties are requested in GetChildren call by CalDAV/CardDAV clients. // The iCalendar/vCard (calendar-data/address-data) is typically requested not in GetChildren, but in a separate multiget // report, in MultiGetAsync, that follow this request. // Bynari submits PROPFIND without props - Engine will request getcontentlength IList<IHierarchyItem> children = new List<IHierarchyItem>(); return new PageResults((await CalendarFile.LoadByCalendarFolderIdAsync(Context, calendarFolderId, PropsToLoad.Minimum)), null); } public async Task<IFile> CreateFileAsync(string name, Stream content, string contentType, long totalFileSize) { // The actual event or to-do object is created in datatbase in CardFile.Write call. return CalendarFile.CreateCalendarFile(Context, calendarFolderId); } public async Task<IFolder> CreateFolderAsync(string name) { throw new DavException("Not allowed.", DavStatus.NOT_ALLOWED); } public override async Task MoveToAsync(IItemCollection destFolder, string destName, MultistatusException multistatus) { // Here we support only calendars renaming. Check that user has permissions to write. string sql = @"UPDATE [cal_CalendarFolder] SET Name=@Name WHERE [CalendarFolderId]=@CalendarFolderId AND [CalendarFolderId] IN (SELECT [CalendarFolderId] FROM [cal_Access] WHERE [UserId] = @UserId AND [Write] = 1)"; if (await Context.ExecuteNonQueryAsync(sql, "@UserId" , Context.UserId , "@CalendarFolderId" , calendarFolderId , "@Name" , destName) < 1) { throw new DavException("Item not found or you do not have enough permissions to complete this operation.", DavStatus.FORBIDDEN); } } public override async Task DeleteAsync(MultistatusException multistatus) { // Delete calendar and all events / to-dos associated with it. Check that user has permissions to delete. string sql = @"DELETE FROM [cal_CalendarFolder] WHERE [CalendarFolderId]=@CalendarFolderId AND [CalendarFolderId] IN (SELECT [CalendarFolderId] FROM [cal_Access] WHERE [UserId] = @UserId AND [Owner] = 1)"; if (await Context.ExecuteNonQueryAsync(sql, "@UserId" , Context.UserId , "@CalendarFolderId" , calendarFolderId) < 1) { throw new DavException("Item not found or you do not have enough permissions to complete this operation.", DavStatus.FORBIDDEN); } } public override async Task<IEnumerable<PropertyValue>> GetPropertiesAsync(IList<PropertyName> names, bool allprop) { IList<PropertyValue> propVals = await GetPropertyValuesAsync( "SELECT [Name], [Namespace], [PropVal] FROM [cal_CalendarFolderProperty] WHERE [CalendarFolderId] = @CalendarFolderId", "@CalendarFolderId", calendarFolderId); if (allprop) { return propVals; } else { IList<PropertyValue> requestedPropVals = new List<PropertyValue>(); foreach (PropertyValue p in propVals) { if (names.Contains(p.QualifiedName)) { requestedPropVals.Add(p); } } return requestedPropVals; } } public override async Task UpdatePropertiesAsync( IList<PropertyValue> setProps, IList<PropertyName> delProps, MultistatusException multistatus) { foreach (PropertyValue p in setProps) { await SetPropertyAsync(p); // create or update property } foreach (PropertyName p in delProps) { await RemovePropertyAsync(p.Name, p.Namespace); } } private async Task<IList<PropertyValue>> GetPropertyValuesAsync(string command, params object[] prms) { List<PropertyValue> l = new List<PropertyValue>(); using (SqlDataReader reader = await Context.ExecuteReaderAsync(command, prms)) { while (await reader.ReadAsync()) { string name = reader.GetString(reader.GetOrdinal("Name")); string ns = reader.GetString(reader.GetOrdinal("Namespace")); string value = reader.GetString(reader.GetOrdinal("PropVal")); l.Add(new PropertyValue(new PropertyName(name, ns), value)); } } return l; } private async Task SetPropertyAsync(PropertyValue prop) { string selectCommand = @"SELECT Count(*) FROM [cal_CalendarFolderProperty] WHERE [CalendarFolderId] = @CalendarFolderId AND [Name] = @Name AND [Namespace] = @Namespace"; int count = await Context.ExecuteScalarAsync<int>( selectCommand, "@CalendarFolderId" , calendarFolderId, "@Name" , prop.QualifiedName.Name, "@Namespace" , prop.QualifiedName.Namespace); // insert if (count == 0) { string insertCommand = @"INSERT INTO [cal_CalendarFolderProperty] ([CalendarFolderId], [Name], [Namespace], [PropVal]) VALUES(@CalendarFolderId, @Name, @Namespace, @PropVal)"; await Context.ExecuteNonQueryAsync( insertCommand, "@PropVal" , prop.Value, "@CalendarFolderId" , calendarFolderId, "@Name" , prop.QualifiedName.Name, "@Namespace" , prop.QualifiedName.Namespace); } else { // update string command = @"UPDATE [cal_CalendarFolderProperty] SET [PropVal] = @PropVal WHERE [CalendarFolderId] = @CalendarFolderId AND [Name] = @Name AND [Namespace] = @Namespace"; await Context.ExecuteNonQueryAsync( command, "@PropVal" , prop.Value, "@CalendarFolderId" , calendarFolderId, "@Name" , prop.QualifiedName.Name, "@Namespace" , prop.QualifiedName.Namespace); } } private async Task RemovePropertyAsync(string name, string ns) { string command = @"DELETE FROM [cal_CalendarFolderProperty] WHERE [CalendarFolderId] = @CalendarFolderId AND [Name] = @Name AND [Namespace] = @Namespace"; await Context.ExecuteNonQueryAsync( command, "@CalendarFolderId" , calendarFolderId, "@Name" , name, "@Namespace" , ns); } public IEnumerable<AppleAllowedSharingMode> AllowedSharingModes { get { return new[] { AppleAllowedSharingMode.CanBePublished, AppleAllowedSharingMode.CanBeShared, }; } } public async Task UpdateSharingAsync(IList<AppleShare> sharesToAddAndRemove) { // Drop all shares first regardless of operation order. When resending // invitations Apple Calendar drops and adds shares for the user in one \ // request. foreach (AppleShare share in sharesToAddAndRemove) { if (share.Operation == AppleSharingOperation.Withdraw) { // remove sharing here // share.Address // share.CommonName } } // Add new shares foreach (AppleShare share in sharesToAddAndRemove) { if (share.Operation != AppleSharingOperation.Withdraw) { // enable sharing and send invitation here // share.Address // share.CommonName } } } public async Task<IEnumerable<SharingInvite>> GetInviteAsync() { IList<SharingInvite> invites = new List<SharingInvite>(); foreach (DataRow rowAccess in rowsAccess) { if (rowAccess.Field<bool>("Owner")) continue; string userId = rowAccess.Field<string>("UserId"); System.Web.Security.MembershipUser user = System.Web.Security.Membership.GetUser(userId); SharingInvite ace = new SharingInvite { Address = string.Format("email:{0}", user.Email) , Access = rowAccess.Field<bool>("Write") ? SharingInviteAccess.ReadWrite : SharingInviteAccess.Read , CommonName = user.UserName , Status = SharingInviteStatus.Accepted }; } return invites; } public async Task<CalendarSharedBy> GetSharedByAsync() { if (rowsAccess.Any(x => !x.Field<bool>("Owner"))) { return CalendarSharedBy.NotShared; } string ownerId = rowsAccess.First(x => x.Field<bool>("Owner")).Field<string>("UserId"); if (ownerId.Equals(Context.UserId, StringComparison.InvariantCultureIgnoreCase)) { return CalendarSharedBy.SharedByOwner; } else { return CalendarSharedBy.Shared; } } public Task SetOwnerAsync(IPrincipal value) { throw new DavException("Not implemented.", DavStatus.NOT_IMPLEMENTED); } public async Task<IPrincipal> GetOwnerAsync() { DataRow rowOwner = rowsAccess.FirstOrDefault(x => x.Field<bool>("Owner") == true); if (rowOwner == null) return null; return await Acl.User.GetUserAsync(Context, rowOwner.Field<string>("UserId")); } public Task SetGroupAsync(IPrincipal value) { throw new DavException("Group cannot be set", DavStatus.FORBIDDEN); } public async Task<IPrincipal> GetGroupAsync() { return null; // Groups are not supported. } public async Task<IEnumerable<SupportedPrivilege>> GetSupportedPrivilegeSetAsync() { return new[] { new SupportedPrivilege { Privilege = Privilege.Read, IsAbstract = false, DescriptionLanguage = "en", Description = "Allows or denies the user the ability to read content and properties of files/folders." }, new SupportedPrivilege { Privilege = Privilege.Write, IsAbstract = false, DescriptionLanguage = "en", Description = "Allows or denies locking an item or modifying the content, properties, or membership of a collection." } }; } public async Task<IEnumerable<Privilege>> GetCurrentUserPrivilegeSetAsync() { DataRow rowAccess = rowsAccess.FirstOrDefault(x => x.Field<string>("UserId") == Context.UserId); if (rowAccess == null) return null; List<Privilege> privileges = new List<Privilege>(); if (rowAccess.Field<bool>("Read")) privileges.Add(Privilege.Read); if (rowAccess.Field<bool>("Write")) privileges.Add(Privilege.Write); return privileges; } public async Task<IEnumerable<ReadAce>> GetAclAsync(IList<PropertyName> propertyNames) { IList<ReadAce> aceList = new List<ReadAce>(); foreach (DataRow rowAccess in rowsAccess) { ReadAce ace = new ReadAce(); ace.Principal = await Acl.User.GetUserAsync(Context, rowAccess.Field<string>("UserId")); if (rowAccess.Field<bool>("Read")) ace.GrantPrivileges.Add(Privilege.Read); if (rowAccess.Field<bool>("Write")) ace.GrantPrivileges.Add(Privilege.Write); ace.IsProtected = rowAccess.Field<bool>("Owner"); aceList.Add(ace); } return aceList; } public Task SetAclAsync(IList<WriteAce> aces) { throw new DavException("Not implemented.", DavStatus.NOT_IMPLEMENTED); } public async Task<AclRestriction> GetAclRestrictionsAsync() { return new AclRestriction { NoInvert = true, GrantOnly = true }; } public async Task<IEnumerable<IHierarchyItem>> GetInheritedAclSetAsync() { return new IHierarchyItem[] { }; } public async Task<IEnumerable<IPrincipalFolder>> GetPrincipalCollectionSetAsync() { return new IPrincipalFolder[] { new Acl.UsersFolder(Context) }; } public async Task<IPrincipal> ResolveWellKnownPrincipalAsync(WellKnownPrincipal wellKnownPrincipal) { return null; } public Task<IEnumerable<IAclHierarchyItem>> GetItemsByPropertyAsync(MatchBy matchBy, IList<PropertyName> props) { throw new DavException("Not implemented.", DavStatus.NOT_IMPLEMENTED); } }