Click or drag to resize

ICalendarFolderAsync Interface

IT Hit WebDAV Classes Reference
Represents a calendar on a CalDAV server.

Namespace:  ITHit.WebDAV.Server.CalDav
Assembly:  ITHit.WebDAV.Server (in ITHit.WebDAV.Server.dll) Version: 11.3.10719
Syntax
public interface ICalendarFolderAsync : ICalendarItem, 
	IFolderAsync, IItemCollectionAsync, IHierarchyItemAsync, IHierarchyItemBaseAsync, ICalendarReportAsync

public interface ICalendarFolderAsync : ICalendarItem, 
	IFolderAsync, IItemCollectionAsync, IHierarchyItemAsync, IHierarchyItemBaseAsync, ICalendarReportAsync

The ICalendarFolderAsync type exposes the following members.

Properties
  NameDescription
Public propertyCalendarDescription
Gets a human-readable description of the calendar collection.
Public propertyCode exampleCreated
Gets the creation date of the item in repository expressed as the coordinated universal time (UTC).
(Inherited from IHierarchyItemBaseAsync.)
Public propertyMaxAttendeesPerInstance
Provides a numeric value indicating the maximum number of ATTENDEE properties in any instance of a calendar object resource stored in a calendar collection.
Public propertyMaxInstances
Gets a numeric value indicating the maximum number of recurrence instances that a calendar object resource stored in a calendar collection can generate.
Public propertyMaxResourceSize
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.
Public propertyCode exampleModified
Gets the last modification date of the item in repository expressed as the coordinated universal time (UTC).
(Inherited from IHierarchyItemBaseAsync.)
Public propertyCode exampleName
Gets the name of the item in repository.
(Inherited from IHierarchyItemBaseAsync.)
Public propertyCode examplePath
Unique item path in the repository relative to storage root.
(Inherited from IHierarchyItemBaseAsync.)
Public propertySupportedComponentTypes
Gets a calendar component types (e.g., VEVENT, VTODO, etc.) that calendar object resources can contain in the calendar collection.
Public propertyUtcMaxDateTime
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.
Public propertyUtcMinDateTime
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.
Top
Methods
  NameDescription
Public methodCode exampleCopyToAsync
Creates a copy of this item with a new name in the destination folder.
(Inherited from IHierarchyItemAsync.)
Public methodCode exampleCreateFileAsync
Creates new WebDAV file with the specified name in this folder.
(Inherited from IFolderAsync.)
Public methodCode exampleCreateFolderAsync
Creates new WebDAV folder with the specified name in this folder.
(Inherited from IFolderAsync.)
Public methodCode exampleDeleteAsync
Deletes this item.
(Inherited from IHierarchyItemAsync.)
Public methodCode exampleGetChildrenAsync
Gets direct children of this folder.
(Inherited from IItemCollectionAsync.)
Public methodCode exampleGetPropertiesAsync
Gets values of all properties or selected properties for this item.
(Inherited from IHierarchyItemAsync.)
Public methodCode exampleGetPropertyNamesAsync
Gets names of all properties for this item.
(Inherited from IHierarchyItemAsync.)
Public methodCode exampleMoveToAsync
Moves this item to the destination folder under a new name.
(Inherited from IHierarchyItemAsync.)
Public methodMultiGetAsync
Gets a list of calendar files that correspont to the specified list of item paths.
(Inherited from ICalendarReportAsync.)
Public methodQueryAsync
Gets a list of calendar files that match specified filter.
(Inherited from ICalendarReportAsync.)
Public methodCode exampleUpdatePropertiesAsync
Adds, modifies and removes properties for this item.
(Inherited from IHierarchyItemAsync.)
Top
Remarks
A calendar folder can contain calendar files only (items that implement ICalendarFileAsync) and folders that are not ICalendarFolderAsync folders. "Nesting" of calendar folders within other calendar folders at any depth is NOT allowed. http://tools.ietf.org/html/rfc4791#section-4.2
Examples

The code below is part of 'CalDAVServer.SqlStorage.AspNet' C# & VB samples provided with the SDK.

// Note:
//  - Mozilla Thunderbird Lightning requires ICurrentUserPrincipalAsync on calendar folder, it does not support discovery.
//  - Outlook CalDAV Synchronizer requires IAclHierarchyItemAsync on calendar folder.

public class CalendarFolder : DavHierarchyItem, ICalendarFolderAsync, IAppleCalendarAsync, ICurrentUserPrincipalAsync, IAclHierarchyItemAsync
{
    public static async Task<ICalendarFolderAsync> 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<ICalendarFolderAsync>> 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<ICalendarFolderAsync>> LoadAsync(DavContext context, string sql, params object[] prms)
    {
        IList<ICalendarFolderAsync> calendarFolders = new List<ICalendarFolderAsync>();

        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 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
            );
    }

    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<ICalendarFileAsync>> 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<ICalendarFileAsync>> 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<ICalendarFileAsync>();
    }

    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<IHierarchyItemAsync> children = new List<IHierarchyItemAsync>();
        return new PageResults((await CalendarFile.LoadByCalendarFolderIdAsync(Context, calendarFolderId, PropsToLoad.Minimum)), null);
    }

    public async Task<IFileAsync> CreateFileAsync(string name)
    {
        // The actual event or to-do object is created in datatbase in CardFile.Write call.
        return CalendarFile.CreateCalendarFile(Context, calendarFolderId);
    }

    public async Task CreateFolderAsync(string name)
    {
        throw new DavException("Not allowed.", DavStatus.NOT_ALLOWED);
    }

    public override async Task MoveToAsync(IItemCollectionAsync 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(IPrincipalAsync value)
    {
        throw new DavException("Not implemented.", DavStatus.NOT_IMPLEMENTED);
    }

    public async Task<IPrincipalAsync> 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(IPrincipalAsync value)
    {
        throw new DavException("Group cannot be set", DavStatus.FORBIDDEN);
    }

    public async Task<IPrincipalAsync> 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<IHierarchyItemAsync>> GetInheritedAclSetAsync()
    {
        return new IHierarchyItemAsync[] { };
    }

    public async Task<IEnumerable<IPrincipalFolderAsync>> GetPrincipalCollectionSetAsync()
    {
        return new IPrincipalFolderAsync[] { new Acl.UsersFolder(Context) };
    }

    public async Task<IPrincipalAsync> ResolveWellKnownPrincipalAsync(WellKnownPrincipal wellKnownPrincipal)
    {
        return null;
    }

    public Task<IEnumerable<IAclHierarchyItemAsync>> GetItemsByPropertyAsync(MatchBy matchBy, IList<PropertyName> props)
    {
        throw new DavException("Not implemented.", DavStatus.NOT_IMPLEMENTED);
    }
}
See Also