Module exchangelib.services

Implement a selection of EWS services (operations).

Exchange is very picky about things like the order of XML elements in SOAP requests, so we need to generate XML automatically instead of taking advantage of Python SOAP libraries and the WSDL file.

Exchange EWS operations overview: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/ews-operations-in-exchange

Expand source code
"""Implement a selection of EWS services (operations).

Exchange is very picky about things like the order of XML elements in SOAP requests, so we need to generate XML
automatically instead of taking advantage of Python SOAP libraries and the WSDL file.

Exchange EWS operations overview:
    https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/ews-operations-in-exchange
"""

from .archive_item import ArchiveItem
from .common import CHUNK_SIZE
from .convert_id import ConvertId
from .copy_item import CopyItem
from .create_attachment import CreateAttachment
from .create_folder import CreateFolder
from .create_item import CreateItem
from .create_user_configuration import CreateUserConfiguration
from .delete_attachment import DeleteAttachment
from .delete_folder import DeleteFolder
from .delete_item import DeleteItem
from .delete_user_configuration import DeleteUserConfiguration
from .empty_folder import EmptyFolder
from .expand_dl import ExpandDL
from .export_items import ExportItems
from .find_folder import FindFolder
from .find_item import FindItem
from .find_people import FindPeople
from .get_attachment import GetAttachment
from .get_delegate import GetDelegate
from .get_events import GetEvents
from .get_folder import GetFolder
from .get_item import GetItem
from .get_mail_tips import GetMailTips
from .get_persona import GetPersona
from .get_room_lists import GetRoomLists
from .get_rooms import GetRooms
from .get_searchable_mailboxes import GetSearchableMailboxes
from .get_server_time_zones import GetServerTimeZones
from .get_streaming_events import GetStreamingEvents
from .get_user_availability import GetUserAvailability
from .get_user_configuration import GetUserConfiguration
from .get_user_oof_settings import GetUserOofSettings
from .mark_as_junk import MarkAsJunk
from .move_folder import MoveFolder
from .move_item import MoveItem
from .resolve_names import ResolveNames
from .send_item import SendItem
from .send_notification import SendNotification
from .set_user_oof_settings import SetUserOofSettings
from .subscribe import SubscribeToStreaming, SubscribeToPull, SubscribeToPush
from .sync_folder_hierarchy import SyncFolderHierarchy
from .sync_folder_items import SyncFolderItems
from .unsubscribe import Unsubscribe
from .update_folder import UpdateFolder
from .update_item import UpdateItem
from .update_user_configuration import UpdateUserConfiguration
from .upload_items import UploadItems

__all__ = [
    'CHUNK_SIZE',
    'ArchiveItem',
    'ConvertId',
    'CopyItem',
    'CreateAttachment',
    'CreateFolder',
    'CreateItem',
    'CreateUserConfiguration',
    'DeleteAttachment',
    'DeleteFolder',
    'DeleteUserConfiguration',
    'DeleteItem',
    'EmptyFolder',
    'ExpandDL',
    'ExportItems',
    'FindFolder',
    'FindItem',
    'FindPeople',
    'GetAttachment',
    'GetDelegate',
    'GetEvents',
    'GetFolder',
    'GetItem',
    'GetMailTips',
    'GetPersona',
    'GetRoomLists',
    'GetRooms',
    'GetSearchableMailboxes',
    'GetServerTimeZones',
    'GetStreamingEvents',
    'GetUserAvailability',
    'GetUserConfiguration',
    'GetUserOofSettings',
    'MarkAsJunk',
    'MoveFolder',
    'MoveItem',
    'ResolveNames',
    'SendItem',
    'SendNotification',
    'SetUserOofSettings',
    'SubscribeToPull',
    'SubscribeToPush',
    'SubscribeToStreaming',
    'SyncFolderHierarchy',
    'SyncFolderItems',
    'Unsubscribe',
    'UpdateFolder',
    'UpdateItem',
    'UpdateUserConfiguration',
    'UploadItems',
]

Sub-modules

exchangelib.services.archive_item
exchangelib.services.common
exchangelib.services.convert_id
exchangelib.services.copy_item
exchangelib.services.create_attachment
exchangelib.services.create_folder
exchangelib.services.create_item
exchangelib.services.create_user_configuration
exchangelib.services.delete_attachment
exchangelib.services.delete_folder
exchangelib.services.delete_item
exchangelib.services.delete_user_configuration
exchangelib.services.empty_folder
exchangelib.services.expand_dl
exchangelib.services.export_items
exchangelib.services.find_folder
exchangelib.services.find_item
exchangelib.services.find_people
exchangelib.services.get_attachment
exchangelib.services.get_delegate
exchangelib.services.get_events
exchangelib.services.get_folder
exchangelib.services.get_item
exchangelib.services.get_mail_tips
exchangelib.services.get_persona
exchangelib.services.get_room_lists
exchangelib.services.get_rooms
exchangelib.services.get_searchable_mailboxes
exchangelib.services.get_server_time_zones
exchangelib.services.get_streaming_events
exchangelib.services.get_user_availability
exchangelib.services.get_user_configuration
exchangelib.services.get_user_oof_settings
exchangelib.services.mark_as_junk
exchangelib.services.move_folder
exchangelib.services.move_item
exchangelib.services.resolve_names
exchangelib.services.send_item
exchangelib.services.send_notification
exchangelib.services.set_user_oof_settings
exchangelib.services.subscribe

The 'Subscribe' service has two different modes, pull and push, with different signatures. Implement as two distinct classes.

exchangelib.services.sync_folder_hierarchy
exchangelib.services.sync_folder_items
exchangelib.services.unsubscribe
exchangelib.services.update_folder
exchangelib.services.update_item
exchangelib.services.update_user_configuration
exchangelib.services.upload_items

Classes

class ArchiveItem (*args, **kwargs)
Expand source code
class ArchiveItem(EWSAccountService):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/archiveitem-operation"""

    SERVICE_NAME = 'ArchiveItem'
    element_container_name = '{%s}Items' % MNS
    supported_from = EXCHANGE_2013

    def call(self, items, to_folder):
        """Move a list of items to a specific folder in the archive mailbox.

        :param items: a list of (id, changekey) tuples or Item objects
        :param to_folder:

        :return: None
        """
        return self._elems_to_objs(self._chunked_get_elements(self.get_payload, items=items, to_folder=to_folder))

    def _elems_to_objs(self, elems):
        from ..items import Item
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            yield Item.id_from_xml(elem)

    def get_payload(self, items, to_folder):
        archiveitem = create_element('m:%s' % self.SERVICE_NAME)
        folder_id = create_folder_ids_element(tag='m:ArchiveSourceFolderId', folders=[to_folder],
                                              version=self.account.version)
        item_ids = create_item_ids_element(items=items, version=self.account.version)
        archiveitem.append(folder_id)
        archiveitem.append(item_ids)
        return archiveitem

Ancestors

Class variables

var SERVICE_NAME
var element_container_name
var supported_from

Methods

def call(self, items, to_folder)

Move a list of items to a specific folder in the archive mailbox.

:param items: a list of (id, changekey) tuples or Item objects :param to_folder:

:return: None

Expand source code
def call(self, items, to_folder):
    """Move a list of items to a specific folder in the archive mailbox.

    :param items: a list of (id, changekey) tuples or Item objects
    :param to_folder:

    :return: None
    """
    return self._elems_to_objs(self._chunked_get_elements(self.get_payload, items=items, to_folder=to_folder))
def get_payload(self, items, to_folder)
Expand source code
def get_payload(self, items, to_folder):
    archiveitem = create_element('m:%s' % self.SERVICE_NAME)
    folder_id = create_folder_ids_element(tag='m:ArchiveSourceFolderId', folders=[to_folder],
                                          version=self.account.version)
    item_ids = create_item_ids_element(items=items, version=self.account.version)
    archiveitem.append(folder_id)
    archiveitem.append(item_ids)
    return archiveitem

Inherited members

class ConvertId (protocol, chunk_size=None, timeout=None)

Take a list of IDs to convert. Returns a list of converted IDs or exception instances, in the same order as the input list.

MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/convertid-operation

Expand source code
class ConvertId(EWSService):
    """Take a list of IDs to convert. Returns a list of converted IDs or exception instances, in the same order as the
    input list.

    MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/convertid-operation
    """

    SERVICE_NAME = 'ConvertId'
    supported_from = EXCHANGE_2007_SP1

    def call(self, items, destination_format):
        if destination_format not in ID_FORMATS:
            raise ValueError("'destination_format' %r must be one of %s" % (destination_format, ID_FORMATS))
        return self._elems_to_objs(
            self._chunked_get_elements(self.get_payload, items=items, destination_format=destination_format)
        )

    def _elems_to_objs(self, elems):
        cls_map = {cls.response_tag(): cls for cls in (
            AlternateId, AlternatePublicFolderId, AlternatePublicFolderItemId
        )}
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            yield cls_map[elem.tag].from_xml(elem, account=None)

    def get_payload(self, items, destination_format):
        supported_item_classes = AlternateId, AlternatePublicFolderId, AlternatePublicFolderItemId
        convertid = create_element('m:%s' % self.SERVICE_NAME, attrs=dict(DestinationFormat=destination_format))
        item_ids = create_element('m:SourceIds')
        for item in items:
            if not isinstance(item, supported_item_classes):
                raise ValueError("'item' value %r must be an instance of %r" % (item, supported_item_classes))
            set_xml_value(item_ids, item, version=self.protocol.version)
        if not len(item_ids):
            raise ValueError('"items" must not be empty')
        convertid.append(item_ids)
        return convertid

    @classmethod
    def _get_elements_in_container(cls, container):
        # We may have other elements in here, e.g. 'ResponseCode'. Filter away those.
        return container.findall(AlternateId.response_tag()) \
            + container.findall(AlternatePublicFolderId.response_tag()) \
            + container.findall(AlternatePublicFolderItemId.response_tag())

Ancestors

Class variables

var SERVICE_NAME
var supported_from

Methods

def call(self, items, destination_format)
Expand source code
def call(self, items, destination_format):
    if destination_format not in ID_FORMATS:
        raise ValueError("'destination_format' %r must be one of %s" % (destination_format, ID_FORMATS))
    return self._elems_to_objs(
        self._chunked_get_elements(self.get_payload, items=items, destination_format=destination_format)
    )
def get_payload(self, items, destination_format)
Expand source code
def get_payload(self, items, destination_format):
    supported_item_classes = AlternateId, AlternatePublicFolderId, AlternatePublicFolderItemId
    convertid = create_element('m:%s' % self.SERVICE_NAME, attrs=dict(DestinationFormat=destination_format))
    item_ids = create_element('m:SourceIds')
    for item in items:
        if not isinstance(item, supported_item_classes):
            raise ValueError("'item' value %r must be an instance of %r" % (item, supported_item_classes))
        set_xml_value(item_ids, item, version=self.protocol.version)
    if not len(item_ids):
        raise ValueError('"items" must not be empty')
    convertid.append(item_ids)
    return convertid

Inherited members

class CopyItem (*args, **kwargs)
Expand source code
class CopyItem(move_item.MoveItem):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/copyitem-operation"""

    SERVICE_NAME = 'CopyItem'

Ancestors

Class variables

var SERVICE_NAME

Inherited members

class CreateAttachment (*args, **kwargs)
Expand source code
class CreateAttachment(EWSAccountService):
    """MSDN:
    https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/createattachment-operation
    """

    SERVICE_NAME = 'CreateAttachment'
    element_container_name = '{%s}Attachments' % MNS

    def call(self, parent_item, items):
        return self._elems_to_objs(self._chunked_get_elements(self.get_payload, items=items, parent_item=parent_item))

    def _elems_to_objs(self, elems):
        from ..attachments import FileAttachment, ItemAttachment
        cls_map = {cls.response_tag(): cls for cls in (FileAttachment, ItemAttachment)}
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            yield cls_map[elem.tag].from_xml(elem=elem, account=self.account)

    def get_payload(self, items, parent_item):
        from ..items import BaseItem
        payload = create_element('m:%s' % self.SERVICE_NAME)
        version = self.account.version
        if isinstance(parent_item, BaseItem):
            # to_item_id() would convert this to a normal ItemId, but the service wants a ParentItemId
            parent_item = ParentItemId(parent_item.id, parent_item.changekey)
        set_xml_value(payload, to_item_id(parent_item, ParentItemId, version=version), version=version)
        attachments = create_element('m:Attachments')
        for item in items:
            set_xml_value(attachments, item, version=self.account.version)
        if not len(attachments):
            raise ValueError('"items" must not be empty')
        payload.append(attachments)
        return payload

Ancestors

Class variables

var SERVICE_NAME
var element_container_name

Methods

def call(self, parent_item, items)
Expand source code
def call(self, parent_item, items):
    return self._elems_to_objs(self._chunked_get_elements(self.get_payload, items=items, parent_item=parent_item))
def get_payload(self, items, parent_item)
Expand source code
def get_payload(self, items, parent_item):
    from ..items import BaseItem
    payload = create_element('m:%s' % self.SERVICE_NAME)
    version = self.account.version
    if isinstance(parent_item, BaseItem):
        # to_item_id() would convert this to a normal ItemId, but the service wants a ParentItemId
        parent_item = ParentItemId(parent_item.id, parent_item.changekey)
    set_xml_value(payload, to_item_id(parent_item, ParentItemId, version=version), version=version)
    attachments = create_element('m:Attachments')
    for item in items:
        set_xml_value(attachments, item, version=self.account.version)
    if not len(attachments):
        raise ValueError('"items" must not be empty')
    payload.append(attachments)
    return payload

Inherited members

class CreateFolder (*args, **kwargs)
Expand source code
class CreateFolder(EWSAccountService):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/createfolder-operation"""

    SERVICE_NAME = 'CreateFolder'
    element_container_name = '{%s}Folders' % MNS

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.folders = []  # A hack to communicate parsing args to _elems_to_objs()

    def call(self, parent_folder, folders):
        # We can't easily find the correct folder class from the returned XML. Instead, return objects with the same
        # class as the folder instance it was requested with.
        self.folders = list(folders)  # Convert to a list, in case 'folders' is a generator. We're iterating twice.
        return self._elems_to_objs(self._chunked_get_elements(
                self.get_payload, items=self.folders, parent_folder=parent_folder,
        ))

    def _elems_to_objs(self, elems):
        for folder, elem in zip(self.folders, elems):
            if isinstance(elem, Exception):
                yield elem
                continue
            yield parse_folder_elem(elem=elem, folder=folder, account=self.account)

    def get_payload(self, folders, parent_folder):
        create_folder = create_element('m:%s' % self.SERVICE_NAME)
        parentfolderid = create_element('m:ParentFolderId')
        set_xml_value(parentfolderid, parent_folder, version=self.account.version)
        set_xml_value(create_folder, parentfolderid, version=self.account.version)
        folder_ids = create_folder_ids_element(tag='m:Folders', folders=folders, version=self.account.version)
        create_folder.append(folder_ids)
        return create_folder

Ancestors

Class variables

var SERVICE_NAME
var element_container_name

Methods

def call(self, parent_folder, folders)
Expand source code
def call(self, parent_folder, folders):
    # We can't easily find the correct folder class from the returned XML. Instead, return objects with the same
    # class as the folder instance it was requested with.
    self.folders = list(folders)  # Convert to a list, in case 'folders' is a generator. We're iterating twice.
    return self._elems_to_objs(self._chunked_get_elements(
            self.get_payload, items=self.folders, parent_folder=parent_folder,
    ))
def get_payload(self, folders, parent_folder)
Expand source code
def get_payload(self, folders, parent_folder):
    create_folder = create_element('m:%s' % self.SERVICE_NAME)
    parentfolderid = create_element('m:ParentFolderId')
    set_xml_value(parentfolderid, parent_folder, version=self.account.version)
    set_xml_value(create_folder, parentfolderid, version=self.account.version)
    folder_ids = create_folder_ids_element(tag='m:Folders', folders=folders, version=self.account.version)
    create_folder.append(folder_ids)
    return create_folder

Inherited members

class CreateItem (*args, **kwargs)

Take a folder and a list of items. Return the result of creation as a list of tuples (success[True|False], errormessage), in the same order as the input list.

MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/createitem-operation

Expand source code
class CreateItem(EWSAccountService):
    """Take a folder and a list of items. Return the result of creation as a list of tuples (success[True|False],
    errormessage), in the same order as the input list.

    MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/createitem-operation
    """

    SERVICE_NAME = 'CreateItem'
    element_container_name = '{%s}Items' % MNS

    def call(self, items, folder, message_disposition, send_meeting_invitations):
        from ..folders import BaseFolder, FolderId
        from ..items import SAVE_ONLY, SEND_AND_SAVE_COPY, SEND_ONLY, \
            SEND_MEETING_INVITATIONS_CHOICES, MESSAGE_DISPOSITION_CHOICES
        if message_disposition not in MESSAGE_DISPOSITION_CHOICES:
            raise ValueError("'message_disposition' %s must be one of %s" % (
                message_disposition, MESSAGE_DISPOSITION_CHOICES
            ))
        if send_meeting_invitations not in SEND_MEETING_INVITATIONS_CHOICES:
            raise ValueError("'send_meeting_invitations' %s must be one of %s" % (
                send_meeting_invitations, SEND_MEETING_INVITATIONS_CHOICES
            ))
        if folder is not None:
            if not isinstance(folder, (BaseFolder, FolderId)):
                raise ValueError("'folder' %r must be a Folder or FolderId instance" % folder)
            if folder.account != self.account:
                raise ValueError('"Folder must belong to this account')
        if message_disposition == SAVE_ONLY and folder is None:
            raise AttributeError("Folder must be supplied when in save-only mode")
        if message_disposition == SEND_AND_SAVE_COPY and folder is None:
            folder = self.account.sent  # 'Sent' is default EWS behaviour
        if message_disposition == SEND_ONLY and folder is not None:
            raise AttributeError("Folder must be None in send-ony mode")
        return self._elems_to_objs(self._chunked_get_elements(
            self.get_payload,
            items=items,
            folder=folder,
            message_disposition=message_disposition,
            send_meeting_invitations=send_meeting_invitations,
        ))

    def _elems_to_objs(self, elems):
        from ..items import BulkCreateResult
        for elem in elems:
            if isinstance(elem, (Exception, type(None))):
                yield elem
                continue
            if isinstance(elem, bool):
                yield elem
                continue
            yield BulkCreateResult.from_xml(elem=elem, account=self.account)

    @classmethod
    def _get_elements_in_container(cls, container):
        res = super()._get_elements_in_container(container)
        return res or [True]

    def get_payload(self, items, folder, message_disposition, send_meeting_invitations):
        """Take a list of Item objects (CalendarItem, Message etc) and return the XML for a CreateItem request.
        convert items to XML Elements.

        MessageDisposition is only applicable to email messages, where it is required.

        SendMeetingInvitations is required for calendar items. It is also applicable to tasks, meeting request
        responses (see
        https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/createitem-operation-meeting-request
        ) and sharing
        invitation accepts (see
        https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/createitem-acceptsharinginvitation
        ). The last two are not supported yet.

        :param items:
        :param folder:
        :param message_disposition:
        :param send_meeting_invitations:
        """
        createitem = create_element(
            'm:%s' % self.SERVICE_NAME,
            attrs=OrderedDict([
                ('MessageDisposition', message_disposition),
                ('SendMeetingInvitations', send_meeting_invitations),
            ])
        )
        if folder:
            saveditemfolderid = create_element('m:SavedItemFolderId')
            set_xml_value(saveditemfolderid, folder, version=self.account.version)
            createitem.append(saveditemfolderid)
        item_elems = create_element('m:Items')
        for item in items:
            if not item.account:
                item.account = self.account
            set_xml_value(item_elems, item, version=self.account.version)
        if not len(item_elems):
            raise ValueError('"items" must not be empty')
        createitem.append(item_elems)
        return createitem

Ancestors

Class variables

var SERVICE_NAME
var element_container_name

Methods

def call(self, items, folder, message_disposition, send_meeting_invitations)
Expand source code
def call(self, items, folder, message_disposition, send_meeting_invitations):
    from ..folders import BaseFolder, FolderId
    from ..items import SAVE_ONLY, SEND_AND_SAVE_COPY, SEND_ONLY, \
        SEND_MEETING_INVITATIONS_CHOICES, MESSAGE_DISPOSITION_CHOICES
    if message_disposition not in MESSAGE_DISPOSITION_CHOICES:
        raise ValueError("'message_disposition' %s must be one of %s" % (
            message_disposition, MESSAGE_DISPOSITION_CHOICES
        ))
    if send_meeting_invitations not in SEND_MEETING_INVITATIONS_CHOICES:
        raise ValueError("'send_meeting_invitations' %s must be one of %s" % (
            send_meeting_invitations, SEND_MEETING_INVITATIONS_CHOICES
        ))
    if folder is not None:
        if not isinstance(folder, (BaseFolder, FolderId)):
            raise ValueError("'folder' %r must be a Folder or FolderId instance" % folder)
        if folder.account != self.account:
            raise ValueError('"Folder must belong to this account')
    if message_disposition == SAVE_ONLY and folder is None:
        raise AttributeError("Folder must be supplied when in save-only mode")
    if message_disposition == SEND_AND_SAVE_COPY and folder is None:
        folder = self.account.sent  # 'Sent' is default EWS behaviour
    if message_disposition == SEND_ONLY and folder is not None:
        raise AttributeError("Folder must be None in send-ony mode")
    return self._elems_to_objs(self._chunked_get_elements(
        self.get_payload,
        items=items,
        folder=folder,
        message_disposition=message_disposition,
        send_meeting_invitations=send_meeting_invitations,
    ))
def get_payload(self, items, folder, message_disposition, send_meeting_invitations)

Take a list of Item objects (CalendarItem, Message etc) and return the XML for a CreateItem request. convert items to XML Elements.

MessageDisposition is only applicable to email messages, where it is required.

SendMeetingInvitations is required for calendar items. It is also applicable to tasks, meeting request responses (see https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/createitem-operation-meeting-request ) and sharing invitation accepts (see https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/createitem-acceptsharinginvitation ). The last two are not supported yet.

:param items: :param folder: :param message_disposition: :param send_meeting_invitations:

Expand source code
def get_payload(self, items, folder, message_disposition, send_meeting_invitations):
    """Take a list of Item objects (CalendarItem, Message etc) and return the XML for a CreateItem request.
    convert items to XML Elements.

    MessageDisposition is only applicable to email messages, where it is required.

    SendMeetingInvitations is required for calendar items. It is also applicable to tasks, meeting request
    responses (see
    https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/createitem-operation-meeting-request
    ) and sharing
    invitation accepts (see
    https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/createitem-acceptsharinginvitation
    ). The last two are not supported yet.

    :param items:
    :param folder:
    :param message_disposition:
    :param send_meeting_invitations:
    """
    createitem = create_element(
        'm:%s' % self.SERVICE_NAME,
        attrs=OrderedDict([
            ('MessageDisposition', message_disposition),
            ('SendMeetingInvitations', send_meeting_invitations),
        ])
    )
    if folder:
        saveditemfolderid = create_element('m:SavedItemFolderId')
        set_xml_value(saveditemfolderid, folder, version=self.account.version)
        createitem.append(saveditemfolderid)
    item_elems = create_element('m:Items')
    for item in items:
        if not item.account:
            item.account = self.account
        set_xml_value(item_elems, item, version=self.account.version)
    if not len(item_elems):
        raise ValueError('"items" must not be empty')
    createitem.append(item_elems)
    return createitem

Inherited members

class CreateUserConfiguration (*args, **kwargs)
Expand source code
class CreateUserConfiguration(EWSAccountService):
    """MSDN:
    https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/createuserconfiguration-operation
    """

    SERVICE_NAME = 'CreateUserConfiguration'
    returns_elements = False

    def call(self, user_configuration):
        return self._get_elements(payload=self.get_payload(user_configuration=user_configuration))

    def get_payload(self, user_configuration):
        createuserconfiguration = create_element('m:%s' % self.SERVICE_NAME)
        set_xml_value(createuserconfiguration, user_configuration, version=self.protocol.version)
        return createuserconfiguration

Ancestors

Class variables

var SERVICE_NAME
var returns_elements

Methods

def call(self, user_configuration)
Expand source code
def call(self, user_configuration):
    return self._get_elements(payload=self.get_payload(user_configuration=user_configuration))
def get_payload(self, user_configuration)
Expand source code
def get_payload(self, user_configuration):
    createuserconfiguration = create_element('m:%s' % self.SERVICE_NAME)
    set_xml_value(createuserconfiguration, user_configuration, version=self.protocol.version)
    return createuserconfiguration

Inherited members

class DeleteAttachment (*args, **kwargs)
Expand source code
class DeleteAttachment(EWSAccountService):
    """MSDN:
    https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/deleteattachment-operation
    """

    SERVICE_NAME = 'DeleteAttachment'

    def call(self, items):
        return self._elems_to_objs(self._chunked_get_elements(self.get_payload, items=items))

    def _elems_to_objs(self, elems):
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            yield RootItemId.from_xml(elem=elem, account=self.account)

    @classmethod
    def _get_elements_in_container(cls, container):
        return container.findall(RootItemId.response_tag())

    def get_payload(self, items):
        payload = create_element('m:%s' % self.SERVICE_NAME)
        attachment_ids = create_attachment_ids_element(items=items, version=self.account.version)
        payload.append(attachment_ids)
        return payload

Ancestors

Class variables

var SERVICE_NAME

Methods

def call(self, items)
Expand source code
def call(self, items):
    return self._elems_to_objs(self._chunked_get_elements(self.get_payload, items=items))
def get_payload(self, items)
Expand source code
def get_payload(self, items):
    payload = create_element('m:%s' % self.SERVICE_NAME)
    attachment_ids = create_attachment_ids_element(items=items, version=self.account.version)
    payload.append(attachment_ids)
    return payload

Inherited members

class DeleteFolder (*args, **kwargs)
Expand source code
class DeleteFolder(EWSAccountService):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/deletefolder-operation"""

    SERVICE_NAME = 'DeleteFolder'
    returns_elements = False

    def call(self, folders, delete_type):
        return self._chunked_get_elements(self.get_payload, items=folders, delete_type=delete_type)

    def get_payload(self, folders, delete_type):
        deletefolder = create_element('m:%s' % self.SERVICE_NAME, attrs=dict(DeleteType=delete_type))
        folder_ids = create_folder_ids_element(tag='m:FolderIds', folders=folders, version=self.account.version)
        deletefolder.append(folder_ids)
        return deletefolder

Ancestors

Class variables

var SERVICE_NAME
var returns_elements

Methods

def call(self, folders, delete_type)
Expand source code
def call(self, folders, delete_type):
    return self._chunked_get_elements(self.get_payload, items=folders, delete_type=delete_type)
def get_payload(self, folders, delete_type)
Expand source code
def get_payload(self, folders, delete_type):
    deletefolder = create_element('m:%s' % self.SERVICE_NAME, attrs=dict(DeleteType=delete_type))
    folder_ids = create_folder_ids_element(tag='m:FolderIds', folders=folders, version=self.account.version)
    deletefolder.append(folder_ids)
    return deletefolder

Inherited members

class DeleteItem (*args, **kwargs)

Take a folder and a list of (id, changekey) tuples. Return result of deletion as a list of tuples (success[True|False], errormessage), in the same order as the input list.

MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/deleteitem-operation

Expand source code
class DeleteItem(EWSAccountService):
    """Take a folder and a list of (id, changekey) tuples. Return result of deletion as a list of tuples
    (success[True|False], errormessage), in the same order as the input list.

    MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/deleteitem-operation
    """

    SERVICE_NAME = 'DeleteItem'
    returns_elements = False

    def call(self, items, delete_type, send_meeting_cancellations, affected_task_occurrences, suppress_read_receipts):
        from ..items import DELETE_TYPE_CHOICES, SEND_MEETING_CANCELLATIONS_CHOICES, AFFECTED_TASK_OCCURRENCES_CHOICES
        if delete_type not in DELETE_TYPE_CHOICES:
            raise ValueError("'delete_type' %s must be one of %s" % (
                delete_type, DELETE_TYPE_CHOICES
            ))
        if send_meeting_cancellations not in SEND_MEETING_CANCELLATIONS_CHOICES:
            raise ValueError("'send_meeting_cancellations' %s must be one of %s" % (
                send_meeting_cancellations, SEND_MEETING_CANCELLATIONS_CHOICES
            ))
        if affected_task_occurrences not in AFFECTED_TASK_OCCURRENCES_CHOICES:
            raise ValueError("'affected_task_occurrences' %s must be one of %s" % (
                affected_task_occurrences, AFFECTED_TASK_OCCURRENCES_CHOICES
            ))
        if suppress_read_receipts not in (True, False):
            raise ValueError("'suppress_read_receipts' %s must be True or False" % suppress_read_receipts)
        return self._chunked_get_elements(
            self.get_payload,
            items=items,
            delete_type=delete_type,
            send_meeting_cancellations=send_meeting_cancellations,
            affected_task_occurrences=affected_task_occurrences,
            suppress_read_receipts=suppress_read_receipts,
        )

    def get_payload(self, items, delete_type, send_meeting_cancellations, affected_task_occurrences,
                    suppress_read_receipts):
        # Takes a list of (id, changekey) tuples or Item objects and returns the XML for a DeleteItem request.
        if self.account.version.build >= EXCHANGE_2013_SP1:
            deleteitem = create_element(
                'm:%s' % self.SERVICE_NAME,
                attrs=OrderedDict([
                    ('DeleteType', delete_type),
                    ('SendMeetingCancellations', send_meeting_cancellations),
                    ('AffectedTaskOccurrences', affected_task_occurrences),
                    ('SuppressReadReceipts', 'true' if suppress_read_receipts else 'false'),
                ])
            )
        else:
            deleteitem = create_element(
                'm:%s' % self.SERVICE_NAME,
                attrs=OrderedDict([
                    ('DeleteType', delete_type),
                    ('SendMeetingCancellations', send_meeting_cancellations),
                    ('AffectedTaskOccurrences', affected_task_occurrences),
                 ])
            )

        item_ids = create_item_ids_element(items=items, version=self.account.version)
        deleteitem.append(item_ids)
        return deleteitem

Ancestors

Class variables

var SERVICE_NAME
var returns_elements

Methods

def call(self, items, delete_type, send_meeting_cancellations, affected_task_occurrences, suppress_read_receipts)
Expand source code
def call(self, items, delete_type, send_meeting_cancellations, affected_task_occurrences, suppress_read_receipts):
    from ..items import DELETE_TYPE_CHOICES, SEND_MEETING_CANCELLATIONS_CHOICES, AFFECTED_TASK_OCCURRENCES_CHOICES
    if delete_type not in DELETE_TYPE_CHOICES:
        raise ValueError("'delete_type' %s must be one of %s" % (
            delete_type, DELETE_TYPE_CHOICES
        ))
    if send_meeting_cancellations not in SEND_MEETING_CANCELLATIONS_CHOICES:
        raise ValueError("'send_meeting_cancellations' %s must be one of %s" % (
            send_meeting_cancellations, SEND_MEETING_CANCELLATIONS_CHOICES
        ))
    if affected_task_occurrences not in AFFECTED_TASK_OCCURRENCES_CHOICES:
        raise ValueError("'affected_task_occurrences' %s must be one of %s" % (
            affected_task_occurrences, AFFECTED_TASK_OCCURRENCES_CHOICES
        ))
    if suppress_read_receipts not in (True, False):
        raise ValueError("'suppress_read_receipts' %s must be True or False" % suppress_read_receipts)
    return self._chunked_get_elements(
        self.get_payload,
        items=items,
        delete_type=delete_type,
        send_meeting_cancellations=send_meeting_cancellations,
        affected_task_occurrences=affected_task_occurrences,
        suppress_read_receipts=suppress_read_receipts,
    )
def get_payload(self, items, delete_type, send_meeting_cancellations, affected_task_occurrences, suppress_read_receipts)
Expand source code
def get_payload(self, items, delete_type, send_meeting_cancellations, affected_task_occurrences,
                suppress_read_receipts):
    # Takes a list of (id, changekey) tuples or Item objects and returns the XML for a DeleteItem request.
    if self.account.version.build >= EXCHANGE_2013_SP1:
        deleteitem = create_element(
            'm:%s' % self.SERVICE_NAME,
            attrs=OrderedDict([
                ('DeleteType', delete_type),
                ('SendMeetingCancellations', send_meeting_cancellations),
                ('AffectedTaskOccurrences', affected_task_occurrences),
                ('SuppressReadReceipts', 'true' if suppress_read_receipts else 'false'),
            ])
        )
    else:
        deleteitem = create_element(
            'm:%s' % self.SERVICE_NAME,
            attrs=OrderedDict([
                ('DeleteType', delete_type),
                ('SendMeetingCancellations', send_meeting_cancellations),
                ('AffectedTaskOccurrences', affected_task_occurrences),
             ])
        )

    item_ids = create_item_ids_element(items=items, version=self.account.version)
    deleteitem.append(item_ids)
    return deleteitem

Inherited members

class DeleteUserConfiguration (*args, **kwargs)
Expand source code
class DeleteUserConfiguration(EWSAccountService):
    """MSDN:
    https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/deleteuserconfiguration-operation
    """

    SERVICE_NAME = 'DeleteUserConfiguration'
    returns_elements = False

    def call(self, user_configuration_name):
        return self._get_elements(payload=self.get_payload(user_configuration_name=user_configuration_name))

    def get_payload(self, user_configuration_name):
        deleteuserconfiguration = create_element('m:%s' % self.SERVICE_NAME)
        set_xml_value(deleteuserconfiguration, user_configuration_name, version=self.account.version)
        return deleteuserconfiguration

Ancestors

Class variables

var SERVICE_NAME
var returns_elements

Methods

def call(self, user_configuration_name)
Expand source code
def call(self, user_configuration_name):
    return self._get_elements(payload=self.get_payload(user_configuration_name=user_configuration_name))
def get_payload(self, user_configuration_name)
Expand source code
def get_payload(self, user_configuration_name):
    deleteuserconfiguration = create_element('m:%s' % self.SERVICE_NAME)
    set_xml_value(deleteuserconfiguration, user_configuration_name, version=self.account.version)
    return deleteuserconfiguration

Inherited members

class EmptyFolder (*args, **kwargs)
Expand source code
class EmptyFolder(EWSAccountService):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/emptyfolder-operation"""

    SERVICE_NAME = 'EmptyFolder'
    returns_elements = False

    def call(self, folders, delete_type, delete_sub_folders):
        return self._chunked_get_elements(
            self.get_payload, items=folders, delete_type=delete_type, delete_sub_folders=delete_sub_folders
        )

    def get_payload(self, folders, delete_type, delete_sub_folders):
        emptyfolder = create_element(
            'm:%s' % self.SERVICE_NAME,
            attrs=OrderedDict([
                ('DeleteType', delete_type),
                ('DeleteSubFolders', 'true' if delete_sub_folders else 'false'),
            ])
        )
        folder_ids = create_folder_ids_element(tag='m:FolderIds', folders=folders, version=self.account.version)
        emptyfolder.append(folder_ids)
        return emptyfolder

Ancestors

Class variables

var SERVICE_NAME
var returns_elements

Methods

def call(self, folders, delete_type, delete_sub_folders)
Expand source code
def call(self, folders, delete_type, delete_sub_folders):
    return self._chunked_get_elements(
        self.get_payload, items=folders, delete_type=delete_type, delete_sub_folders=delete_sub_folders
    )
def get_payload(self, folders, delete_type, delete_sub_folders)
Expand source code
def get_payload(self, folders, delete_type, delete_sub_folders):
    emptyfolder = create_element(
        'm:%s' % self.SERVICE_NAME,
        attrs=OrderedDict([
            ('DeleteType', delete_type),
            ('DeleteSubFolders', 'true' if delete_sub_folders else 'false'),
        ])
    )
    folder_ids = create_folder_ids_element(tag='m:FolderIds', folders=folders, version=self.account.version)
    emptyfolder.append(folder_ids)
    return emptyfolder

Inherited members

class ExpandDL (protocol, chunk_size=None, timeout=None)
Expand source code
class ExpandDL(EWSService):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/expanddl-operation"""

    SERVICE_NAME = 'ExpandDL'
    element_container_name = '{%s}DLExpansion' % MNS
    WARNINGS_TO_IGNORE_IN_RESPONSE = ErrorNameResolutionMultipleResults

    def call(self, distribution_list):
        return self._elems_to_objs(self._get_elements(payload=self.get_payload(distribution_list=distribution_list)))

    def _elems_to_objs(self, elems):
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            yield Mailbox.from_xml(elem, account=None)

    def get_payload(self, distribution_list):
        payload = create_element('m:%s' % self.SERVICE_NAME)
        set_xml_value(payload, distribution_list, version=self.protocol.version)
        return payload

Ancestors

Class variables

var SERVICE_NAME
var WARNINGS_TO_IGNORE_IN_RESPONSE

Global error type within this module.

var element_container_name

Methods

def call(self, distribution_list)
Expand source code
def call(self, distribution_list):
    return self._elems_to_objs(self._get_elements(payload=self.get_payload(distribution_list=distribution_list)))
def get_payload(self, distribution_list)
Expand source code
def get_payload(self, distribution_list):
    payload = create_element('m:%s' % self.SERVICE_NAME)
    set_xml_value(payload, distribution_list, version=self.protocol.version)
    return payload

Inherited members

class ExportItems (*args, **kwargs)
Expand source code
class ExportItems(EWSAccountService):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/exportitems-operation"""

    ERRORS_TO_CATCH_IN_RESPONSE = ResponseMessageError
    SERVICE_NAME = 'ExportItems'
    element_container_name = '{%s}Data' % MNS

    def call(self, items):
        return self._elems_to_objs(self._chunked_get_elements(self.get_payload, items=items))

    def _elems_to_objs(self, elems):
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            yield elem.text  # All we want is the 64bit string in the 'Data' tag

    def get_payload(self, items):
        exportitems = create_element('m:%s' % self.SERVICE_NAME)
        item_ids = create_item_ids_element(items=items, version=self.account.version)
        exportitems.append(item_ids)
        return exportitems

    # We need to override this since ExportItemsResponseMessage is formatted a
    # little bit differently. .
    @classmethod
    def _get_elements_in_container(cls, container):
        return [container]

Ancestors

Class variables

var ERRORS_TO_CATCH_IN_RESPONSE

Global error type within this module.

var SERVICE_NAME
var element_container_name

Methods

def call(self, items)
Expand source code
def call(self, items):
    return self._elems_to_objs(self._chunked_get_elements(self.get_payload, items=items))
def get_payload(self, items)
Expand source code
def get_payload(self, items):
    exportitems = create_element('m:%s' % self.SERVICE_NAME)
    item_ids = create_item_ids_element(items=items, version=self.account.version)
    exportitems.append(item_ids)
    return exportitems

Inherited members

class FindFolder (*args, **kwargs)
Expand source code
class FindFolder(EWSAccountService):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/findfolder-operation"""

    SERVICE_NAME = 'FindFolder'
    element_container_name = '{%s}Folders' % TNS
    paging_container_name = '{%s}RootFolder' % MNS
    supports_paging = True

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.root = None  # A hack to communicate parsing args to _elems_to_objs()

    def call(self, folders, additional_fields, restriction, shape, depth, max_items, offset):
        """Find subfolders of a folder.

        :param folders: the folders to act on
        :param additional_fields: the extra fields that should be returned with the folder, as FieldPath objects
        :param restriction: Restriction object that defines the filters for the query
        :param shape: The set of attributes to return
        :param depth: How deep in the folder structure to search for folders
        :param max_items: The maximum number of items to return
        :param offset: the offset relative to the first item in the item collection. Usually 0.

        :return: XML elements for the matching folders
        """
        roots = {f.root for f in folders}
        if len(roots) != 1:
            raise ValueError('FindFolder must be called with folders in the same root hierarchy (%r)' % roots)
        self.root = roots.pop()
        return self._elems_to_objs(self._paged_call(
                payload_func=self.get_payload,
                max_items=max_items,
                folders=folders,
                **dict(
                    additional_fields=additional_fields,
                    restriction=restriction,
                    shape=shape,
                    depth=depth,
                    page_size=self.chunk_size,
                    offset=offset,
                )
        ))

    def _elems_to_objs(self, elems):
        from ..folders import Folder
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            yield Folder.from_xml_with_root(elem=elem, root=self.root)

    def get_payload(self, folders, additional_fields, restriction, shape, depth, page_size, offset=0):
        findfolder = create_element('m:%s' % self.SERVICE_NAME, attrs=dict(Traversal=depth))
        foldershape = create_shape_element(
            tag='m:FolderShape', shape=shape, additional_fields=additional_fields, version=self.account.version
        )
        findfolder.append(foldershape)
        if self.account.version.build >= EXCHANGE_2010:
            indexedpageviewitem = create_element(
                'm:IndexedPageFolderView',
                attrs=OrderedDict([
                    ('MaxEntriesReturned', str(page_size)),
                    ('Offset', str(offset)),
                    ('BasePoint', 'Beginning'),
                ])
            )
            findfolder.append(indexedpageviewitem)
        else:
            if offset != 0:
                raise ValueError('Offsets are only supported from Exchange 2010')
        if restriction:
            findfolder.append(restriction.to_xml(version=self.account.version))
        parentfolderids = create_element('m:ParentFolderIds')
        set_xml_value(parentfolderids, folders, version=self.account.version)
        findfolder.append(parentfolderids)
        return findfolder

Ancestors

Class variables

var SERVICE_NAME
var element_container_name
var paging_container_name
var supports_paging

Methods

def call(self, folders, additional_fields, restriction, shape, depth, max_items, offset)

Find subfolders of a folder.

:param folders: the folders to act on :param additional_fields: the extra fields that should be returned with the folder, as FieldPath objects :param restriction: Restriction object that defines the filters for the query :param shape: The set of attributes to return :param depth: How deep in the folder structure to search for folders :param max_items: The maximum number of items to return :param offset: the offset relative to the first item in the item collection. Usually 0.

:return: XML elements for the matching folders

Expand source code
def call(self, folders, additional_fields, restriction, shape, depth, max_items, offset):
    """Find subfolders of a folder.

    :param folders: the folders to act on
    :param additional_fields: the extra fields that should be returned with the folder, as FieldPath objects
    :param restriction: Restriction object that defines the filters for the query
    :param shape: The set of attributes to return
    :param depth: How deep in the folder structure to search for folders
    :param max_items: The maximum number of items to return
    :param offset: the offset relative to the first item in the item collection. Usually 0.

    :return: XML elements for the matching folders
    """
    roots = {f.root for f in folders}
    if len(roots) != 1:
        raise ValueError('FindFolder must be called with folders in the same root hierarchy (%r)' % roots)
    self.root = roots.pop()
    return self._elems_to_objs(self._paged_call(
            payload_func=self.get_payload,
            max_items=max_items,
            folders=folders,
            **dict(
                additional_fields=additional_fields,
                restriction=restriction,
                shape=shape,
                depth=depth,
                page_size=self.chunk_size,
                offset=offset,
            )
    ))
def get_payload(self, folders, additional_fields, restriction, shape, depth, page_size, offset=0)
Expand source code
def get_payload(self, folders, additional_fields, restriction, shape, depth, page_size, offset=0):
    findfolder = create_element('m:%s' % self.SERVICE_NAME, attrs=dict(Traversal=depth))
    foldershape = create_shape_element(
        tag='m:FolderShape', shape=shape, additional_fields=additional_fields, version=self.account.version
    )
    findfolder.append(foldershape)
    if self.account.version.build >= EXCHANGE_2010:
        indexedpageviewitem = create_element(
            'm:IndexedPageFolderView',
            attrs=OrderedDict([
                ('MaxEntriesReturned', str(page_size)),
                ('Offset', str(offset)),
                ('BasePoint', 'Beginning'),
            ])
        )
        findfolder.append(indexedpageviewitem)
    else:
        if offset != 0:
            raise ValueError('Offsets are only supported from Exchange 2010')
    if restriction:
        findfolder.append(restriction.to_xml(version=self.account.version))
    parentfolderids = create_element('m:ParentFolderIds')
    set_xml_value(parentfolderids, folders, version=self.account.version)
    findfolder.append(parentfolderids)
    return findfolder

Inherited members

class FindItem (*args, **kwargs)
Expand source code
class FindItem(EWSAccountService):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/finditem-operation"""

    SERVICE_NAME = 'FindItem'
    element_container_name = '{%s}Items' % TNS
    paging_container_name = '{%s}RootFolder' % MNS
    supports_paging = True

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # A hack to communicate parsing args to _elems_to_objs()
        self.additional_fields = None
        self.shape = None

    def call(self, folders, additional_fields, restriction, order_fields, shape, query_string, depth, calendar_view,
             max_items, offset):
        """Find items in an account.

        :param folders: the folders to act on
        :param additional_fields: the extra fields that should be returned with the item, as FieldPath objects
        :param restriction: a Restriction object for
        :param order_fields: the fields to sort the results by
        :param shape: The set of attributes to return
        :param query_string: a QueryString object
        :param depth: How deep in the folder structure to search for items
        :param calendar_view: If set, returns recurring calendar items unfolded
        :param max_items: the max number of items to return
        :param offset: the offset relative to the first item in the item collection. Usually 0.

        :return: XML elements for the matching items
        """
        self.additional_fields = additional_fields
        self.shape = shape
        return self._elems_to_objs(self._paged_call(
            payload_func=self.get_payload,
            max_items=max_items,
            folders=folders,
            **dict(
                additional_fields=additional_fields,
                restriction=restriction,
                order_fields=order_fields,
                query_string=query_string,
                shape=shape,
                depth=depth,
                calendar_view=calendar_view,
                page_size=self.chunk_size,
                offset=offset,
            )
        ))

    def _elems_to_objs(self, elems):
        from ..folders.base import BaseFolder
        from ..items import Item, ID_ONLY
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            if self.shape == ID_ONLY and self.additional_fields is None:
                yield Item.id_from_xml(elem)
                continue
            yield BaseFolder.item_model_from_tag(elem.tag).from_xml(elem=elem, account=self.account)

    def get_payload(self, folders, additional_fields, restriction, order_fields, query_string, shape, depth,
                    calendar_view, page_size, offset=0):
        finditem = create_element('m:%s' % self.SERVICE_NAME, attrs=dict(Traversal=depth))
        itemshape = create_shape_element(
            tag='m:ItemShape', shape=shape, additional_fields=additional_fields, version=self.account.version
        )
        finditem.append(itemshape)
        if calendar_view is None:
            view_type = create_element(
                'm:IndexedPageItemView',
                attrs=OrderedDict([
                    ('MaxEntriesReturned', str(page_size)),
                    ('Offset', str(offset)),
                    ('BasePoint', 'Beginning'),
                ])
            )
        else:
            view_type = calendar_view.to_xml(version=self.account.version)
        finditem.append(view_type)
        if restriction:
            finditem.append(restriction.to_xml(version=self.account.version))
        if order_fields:
            finditem.append(set_xml_value(
                create_element('m:SortOrder'),
                order_fields,
                version=self.account.version
            ))
        finditem.append(set_xml_value(
            create_element('m:ParentFolderIds'),
            folders,
            version=self.account.version
        ))
        if query_string:
            finditem.append(query_string.to_xml(version=self.account.version))
        return finditem

Ancestors

Class variables

var SERVICE_NAME
var element_container_name
var paging_container_name
var supports_paging

Methods

def call(self, folders, additional_fields, restriction, order_fields, shape, query_string, depth, calendar_view, max_items, offset)

Find items in an account.

:param folders: the folders to act on :param additional_fields: the extra fields that should be returned with the item, as FieldPath objects :param restriction: a Restriction object for :param order_fields: the fields to sort the results by :param shape: The set of attributes to return :param query_string: a QueryString object :param depth: How deep in the folder structure to search for items :param calendar_view: If set, returns recurring calendar items unfolded :param max_items: the max number of items to return :param offset: the offset relative to the first item in the item collection. Usually 0.

:return: XML elements for the matching items

Expand source code
def call(self, folders, additional_fields, restriction, order_fields, shape, query_string, depth, calendar_view,
         max_items, offset):
    """Find items in an account.

    :param folders: the folders to act on
    :param additional_fields: the extra fields that should be returned with the item, as FieldPath objects
    :param restriction: a Restriction object for
    :param order_fields: the fields to sort the results by
    :param shape: The set of attributes to return
    :param query_string: a QueryString object
    :param depth: How deep in the folder structure to search for items
    :param calendar_view: If set, returns recurring calendar items unfolded
    :param max_items: the max number of items to return
    :param offset: the offset relative to the first item in the item collection. Usually 0.

    :return: XML elements for the matching items
    """
    self.additional_fields = additional_fields
    self.shape = shape
    return self._elems_to_objs(self._paged_call(
        payload_func=self.get_payload,
        max_items=max_items,
        folders=folders,
        **dict(
            additional_fields=additional_fields,
            restriction=restriction,
            order_fields=order_fields,
            query_string=query_string,
            shape=shape,
            depth=depth,
            calendar_view=calendar_view,
            page_size=self.chunk_size,
            offset=offset,
        )
    ))
def get_payload(self, folders, additional_fields, restriction, order_fields, query_string, shape, depth, calendar_view, page_size, offset=0)
Expand source code
def get_payload(self, folders, additional_fields, restriction, order_fields, query_string, shape, depth,
                calendar_view, page_size, offset=0):
    finditem = create_element('m:%s' % self.SERVICE_NAME, attrs=dict(Traversal=depth))
    itemshape = create_shape_element(
        tag='m:ItemShape', shape=shape, additional_fields=additional_fields, version=self.account.version
    )
    finditem.append(itemshape)
    if calendar_view is None:
        view_type = create_element(
            'm:IndexedPageItemView',
            attrs=OrderedDict([
                ('MaxEntriesReturned', str(page_size)),
                ('Offset', str(offset)),
                ('BasePoint', 'Beginning'),
            ])
        )
    else:
        view_type = calendar_view.to_xml(version=self.account.version)
    finditem.append(view_type)
    if restriction:
        finditem.append(restriction.to_xml(version=self.account.version))
    if order_fields:
        finditem.append(set_xml_value(
            create_element('m:SortOrder'),
            order_fields,
            version=self.account.version
        ))
    finditem.append(set_xml_value(
        create_element('m:ParentFolderIds'),
        folders,
        version=self.account.version
    ))
    if query_string:
        finditem.append(query_string.to_xml(version=self.account.version))
    return finditem

Inherited members

class FindPeople (*args, **kwargs)
Expand source code
class FindPeople(EWSAccountService):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/findpeople-operation"""

    SERVICE_NAME = 'FindPeople'
    element_container_name = '{%s}People' % MNS
    supported_from = EXCHANGE_2013
    supports_paging = True

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # A hack to communicate parsing args to _elems_to_objs()
        self.additional_fields = None
        self.shape = None

    def call(self, folder, additional_fields, restriction, order_fields, shape, query_string, depth, max_items, offset):
        """Find items in an account.

        :param folder: the Folder object to query
        :param additional_fields: the extra fields that should be returned with the item, as FieldPath objects
        :param restriction: a Restriction object for
        :param order_fields: the fields to sort the results by
        :param shape: The set of attributes to return
        :param query_string: a QueryString object
        :param depth: How deep in the folder structure to search for items
        :param max_items: the max number of items to return
        :param offset: the offset relative to the first item in the item collection. Usually 0.

        :return: XML elements for the matching items
        """
        self.additional_fields = additional_fields
        self.shape = shape
        return self._elems_to_objs(self._paged_call(
            payload_func=self.get_payload,
            max_items=max_items,
            folders=[folder],  # We can only query one folder, so there will only be one element in response
            **dict(
                additional_fields=additional_fields,
                restriction=restriction,
                order_fields=order_fields,
                query_string=query_string,
                shape=shape,
                depth=depth,
                page_size=self.chunk_size,
                offset=offset,
            )
        ))

    def _elems_to_objs(self, elems):
        from ..items import Persona, ID_ONLY
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            if self.shape == ID_ONLY and self.additional_fields is None:
                yield Persona.id_from_xml(elem)
                continue
            yield Persona.from_xml(elem, account=self.account)

    def get_payload(self, folders, additional_fields, restriction, order_fields, query_string, shape, depth, page_size,
                    offset=0):
        folders = list(folders)
        if len(folders) != 1:
            raise ValueError('%r can only query one folder' % self.SERVICE_NAME)
        folder = folders[0]
        findpeople = create_element('m:%s' % self.SERVICE_NAME, attrs=dict(Traversal=depth))
        personashape = create_shape_element(
            tag='m:PersonaShape', shape=shape, additional_fields=additional_fields, version=self.account.version
        )
        findpeople.append(personashape)
        view_type = create_element(
            'm:IndexedPageItemView',
            attrs=OrderedDict([
                ('MaxEntriesReturned', str(page_size)),
                ('Offset', str(offset)),
                ('BasePoint', 'Beginning'),
            ])
        )
        findpeople.append(view_type)
        if restriction:
            findpeople.append(restriction.to_xml(version=self.account.version))
        if order_fields:
            findpeople.append(set_xml_value(
                create_element('m:SortOrder'),
                order_fields,
                version=self.account.version
            ))
        findpeople.append(set_xml_value(
            create_element('m:ParentFolderId'),
            folder,
            version=self.account.version
        ))
        if query_string:
            findpeople.append(query_string.to_xml(version=self.account.version))
        return findpeople

    @staticmethod
    def _get_paging_values(elem):
        """Find paging values. The paging element from FindPeople is different from other paging containers."""
        item_count = int(elem.find('{%s}TotalNumberOfPeopleInView' % MNS).text)
        first_matching = int(elem.find('{%s}FirstMatchingRowIndex' % MNS).text)
        first_loaded = int(elem.find('{%s}FirstLoadedRowIndex' % MNS).text)
        log.debug('Got page with total items %s, first matching %s, first loaded %s ', item_count, first_matching,
                  first_loaded)
        next_offset = None  # GetPersona does not support fetching more pages
        return item_count, next_offset

Ancestors

Class variables

var SERVICE_NAME
var element_container_name
var supported_from
var supports_paging

Methods

def call(self, folder, additional_fields, restriction, order_fields, shape, query_string, depth, max_items, offset)

Find items in an account.

:param folder: the Folder object to query :param additional_fields: the extra fields that should be returned with the item, as FieldPath objects :param restriction: a Restriction object for :param order_fields: the fields to sort the results by :param shape: The set of attributes to return :param query_string: a QueryString object :param depth: How deep in the folder structure to search for items :param max_items: the max number of items to return :param offset: the offset relative to the first item in the item collection. Usually 0.

:return: XML elements for the matching items

Expand source code
def call(self, folder, additional_fields, restriction, order_fields, shape, query_string, depth, max_items, offset):
    """Find items in an account.

    :param folder: the Folder object to query
    :param additional_fields: the extra fields that should be returned with the item, as FieldPath objects
    :param restriction: a Restriction object for
    :param order_fields: the fields to sort the results by
    :param shape: The set of attributes to return
    :param query_string: a QueryString object
    :param depth: How deep in the folder structure to search for items
    :param max_items: the max number of items to return
    :param offset: the offset relative to the first item in the item collection. Usually 0.

    :return: XML elements for the matching items
    """
    self.additional_fields = additional_fields
    self.shape = shape
    return self._elems_to_objs(self._paged_call(
        payload_func=self.get_payload,
        max_items=max_items,
        folders=[folder],  # We can only query one folder, so there will only be one element in response
        **dict(
            additional_fields=additional_fields,
            restriction=restriction,
            order_fields=order_fields,
            query_string=query_string,
            shape=shape,
            depth=depth,
            page_size=self.chunk_size,
            offset=offset,
        )
    ))
def get_payload(self, folders, additional_fields, restriction, order_fields, query_string, shape, depth, page_size, offset=0)
Expand source code
def get_payload(self, folders, additional_fields, restriction, order_fields, query_string, shape, depth, page_size,
                offset=0):
    folders = list(folders)
    if len(folders) != 1:
        raise ValueError('%r can only query one folder' % self.SERVICE_NAME)
    folder = folders[0]
    findpeople = create_element('m:%s' % self.SERVICE_NAME, attrs=dict(Traversal=depth))
    personashape = create_shape_element(
        tag='m:PersonaShape', shape=shape, additional_fields=additional_fields, version=self.account.version
    )
    findpeople.append(personashape)
    view_type = create_element(
        'm:IndexedPageItemView',
        attrs=OrderedDict([
            ('MaxEntriesReturned', str(page_size)),
            ('Offset', str(offset)),
            ('BasePoint', 'Beginning'),
        ])
    )
    findpeople.append(view_type)
    if restriction:
        findpeople.append(restriction.to_xml(version=self.account.version))
    if order_fields:
        findpeople.append(set_xml_value(
            create_element('m:SortOrder'),
            order_fields,
            version=self.account.version
        ))
    findpeople.append(set_xml_value(
        create_element('m:ParentFolderId'),
        folder,
        version=self.account.version
    ))
    if query_string:
        findpeople.append(query_string.to_xml(version=self.account.version))
    return findpeople

Inherited members

class GetAttachment (*args, **kwargs)
Expand source code
class GetAttachment(EWSAccountService):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/getattachment-operation"""

    SERVICE_NAME = 'GetAttachment'
    element_container_name = '{%s}Attachments' % MNS

    def call(self, items, include_mime_content, body_type, filter_html_content, additional_fields):
        if body_type and body_type not in BODY_TYPE_CHOICES:
            raise ValueError("'body_type' %s must be one of %s" % (body_type, BODY_TYPE_CHOICES))
        return self._elems_to_objs(self._chunked_get_elements(
            self.get_payload, items=items, include_mime_content=include_mime_content,
            body_type=body_type, filter_html_content=filter_html_content, additional_fields=additional_fields,
        ))

    def _elems_to_objs(self, elems):
        from ..attachments import FileAttachment, ItemAttachment
        cls_map = {cls.response_tag(): cls for cls in (FileAttachment, ItemAttachment)}
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            yield cls_map[elem.tag].from_xml(elem=elem, account=self.account)

    def get_payload(self, items, include_mime_content, body_type, filter_html_content, additional_fields):
        payload = create_element('m:%s' % self.SERVICE_NAME)
        shape_elem = create_element('m:AttachmentShape')
        if include_mime_content:
            add_xml_child(shape_elem, 't:IncludeMimeContent', 'true')
        if body_type:
            add_xml_child(shape_elem, 't:BodyType', body_type)
        if filter_html_content is not None:
            add_xml_child(shape_elem, 't:FilterHtmlContent', 'true' if filter_html_content else 'false')
        if additional_fields:
            additional_properties = create_element('t:AdditionalProperties')
            expanded_fields = chain(*(f.expand(version=self.account.version) for f in additional_fields))
            set_xml_value(additional_properties, sorted(
                expanded_fields,
                key=lambda f: (getattr(f.field, 'field_uri', ''), f.path)
            ), version=self.account.version)
            shape_elem.append(additional_properties)
        if len(shape_elem):
            payload.append(shape_elem)
        attachment_ids = create_attachment_ids_element(items=items, version=self.account.version)
        payload.append(attachment_ids)
        return payload

    def _update_api_version(self, api_version, header, **parse_opts):
        if not parse_opts.get('stream_file_content', False):
            super()._update_api_version(api_version, header, **parse_opts)
        # TODO: We're skipping this part in streaming mode because StreamingBase64Parser cannot parse the SOAP header

    @classmethod
    def _get_soap_parts(cls, response, **parse_opts):
        if not parse_opts.get('stream_file_content', False):
            return super()._get_soap_parts(response, **parse_opts)

        # Pass the response unaltered. We want to use our custom streaming parser
        return None, response

    def _get_soap_messages(self, body, **parse_opts):
        if not parse_opts.get('stream_file_content', False):
            return super()._get_soap_messages(body, **parse_opts)

        from ..attachments import FileAttachment
        # 'body' is actually the raw response passed on by '_get_soap_parts'
        r = body
        parser = StreamingBase64Parser()
        field = FileAttachment.get_field_by_fieldname('_content')
        handler = StreamingContentHandler(parser=parser, ns=field.namespace, element_name=field.field_uri)
        parser.setContentHandler(handler)
        return parser.parse(r)

    def stream_file_content(self, attachment_id):
        # The streaming XML parser can only stream content of one attachment
        payload = self.get_payload(
            items=[attachment_id], include_mime_content=False, body_type=None, filter_html_content=None,
            additional_fields=None,
        )
        self.streaming = True
        try:
            yield from self._get_response_xml(payload=payload, stream_file_content=True)
        except ElementNotFound as enf:
            # When the returned XML does not contain a Content element, ElementNotFound is thrown by parser.parse().
            # Let the non-streaming SOAP parser parse the response and hook into the normal exception handling.
            # Wrap in DummyResponse because _get_soap_parts() expects an iter_content() method.
            response = DummyResponse(url=None, headers=None, request_headers=None, content=enf.data)
            _, body = super()._get_soap_parts(response=response)
            res = super()._get_soap_messages(body=body)
            for e in self._get_elements_in_response(response=res):
                if isinstance(e, Exception):
                    raise e
            # The returned content did not contain any EWS exceptions. Give up and re-raise the original exception.
            raise enf
        finally:
            self.streaming = False
            self.stop_streaming()

Ancestors

Class variables

var SERVICE_NAME
var element_container_name

Methods

def call(self, items, include_mime_content, body_type, filter_html_content, additional_fields)
Expand source code
def call(self, items, include_mime_content, body_type, filter_html_content, additional_fields):
    if body_type and body_type not in BODY_TYPE_CHOICES:
        raise ValueError("'body_type' %s must be one of %s" % (body_type, BODY_TYPE_CHOICES))
    return self._elems_to_objs(self._chunked_get_elements(
        self.get_payload, items=items, include_mime_content=include_mime_content,
        body_type=body_type, filter_html_content=filter_html_content, additional_fields=additional_fields,
    ))
def get_payload(self, items, include_mime_content, body_type, filter_html_content, additional_fields)
Expand source code
def get_payload(self, items, include_mime_content, body_type, filter_html_content, additional_fields):
    payload = create_element('m:%s' % self.SERVICE_NAME)
    shape_elem = create_element('m:AttachmentShape')
    if include_mime_content:
        add_xml_child(shape_elem, 't:IncludeMimeContent', 'true')
    if body_type:
        add_xml_child(shape_elem, 't:BodyType', body_type)
    if filter_html_content is not None:
        add_xml_child(shape_elem, 't:FilterHtmlContent', 'true' if filter_html_content else 'false')
    if additional_fields:
        additional_properties = create_element('t:AdditionalProperties')
        expanded_fields = chain(*(f.expand(version=self.account.version) for f in additional_fields))
        set_xml_value(additional_properties, sorted(
            expanded_fields,
            key=lambda f: (getattr(f.field, 'field_uri', ''), f.path)
        ), version=self.account.version)
        shape_elem.append(additional_properties)
    if len(shape_elem):
        payload.append(shape_elem)
    attachment_ids = create_attachment_ids_element(items=items, version=self.account.version)
    payload.append(attachment_ids)
    return payload
def stream_file_content(self, attachment_id)
Expand source code
def stream_file_content(self, attachment_id):
    # The streaming XML parser can only stream content of one attachment
    payload = self.get_payload(
        items=[attachment_id], include_mime_content=False, body_type=None, filter_html_content=None,
        additional_fields=None,
    )
    self.streaming = True
    try:
        yield from self._get_response_xml(payload=payload, stream_file_content=True)
    except ElementNotFound as enf:
        # When the returned XML does not contain a Content element, ElementNotFound is thrown by parser.parse().
        # Let the non-streaming SOAP parser parse the response and hook into the normal exception handling.
        # Wrap in DummyResponse because _get_soap_parts() expects an iter_content() method.
        response = DummyResponse(url=None, headers=None, request_headers=None, content=enf.data)
        _, body = super()._get_soap_parts(response=response)
        res = super()._get_soap_messages(body=body)
        for e in self._get_elements_in_response(response=res):
            if isinstance(e, Exception):
                raise e
        # The returned content did not contain any EWS exceptions. Give up and re-raise the original exception.
        raise enf
    finally:
        self.streaming = False
        self.stop_streaming()

Inherited members

class GetDelegate (*args, **kwargs)
Expand source code
class GetDelegate(EWSAccountService):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/getdelegate-operation"""

    SERVICE_NAME = 'GetDelegate'
    supported_from = EXCHANGE_2007_SP1

    def call(self, user_ids, include_permissions):
        return self._elems_to_objs(self._chunked_get_elements(
            self.get_payload,
            items=user_ids or [None],
            mailbox=DLMailbox(email_address=self.account.primary_smtp_address),
            include_permissions=include_permissions,
        ))

    def _elems_to_objs(self, elems):
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            yield DelegateUser.from_xml(elem=elem, account=self.account)

    def get_payload(self, user_ids, mailbox, include_permissions):
        payload = create_element(
            'm:%s' % self.SERVICE_NAME,
            attrs=dict(IncludePermissions='true' if include_permissions else 'false'),
        )
        set_xml_value(payload, mailbox, version=self.protocol.version)
        if user_ids != [None]:
            set_xml_value(payload, user_ids, version=self.protocol.version)
        return payload

    @classmethod
    def _get_elements_in_container(cls, container):
        return container.findall(DelegateUser.response_tag())

    @classmethod
    def _response_message_tag(cls):
        return '{%s}DelegateUserResponseMessageType' % MNS

Ancestors

Class variables

var SERVICE_NAME
var supported_from

Methods

def call(self, user_ids, include_permissions)
Expand source code
def call(self, user_ids, include_permissions):
    return self._elems_to_objs(self._chunked_get_elements(
        self.get_payload,
        items=user_ids or [None],
        mailbox=DLMailbox(email_address=self.account.primary_smtp_address),
        include_permissions=include_permissions,
    ))
def get_payload(self, user_ids, mailbox, include_permissions)
Expand source code
def get_payload(self, user_ids, mailbox, include_permissions):
    payload = create_element(
        'm:%s' % self.SERVICE_NAME,
        attrs=dict(IncludePermissions='true' if include_permissions else 'false'),
    )
    set_xml_value(payload, mailbox, version=self.protocol.version)
    if user_ids != [None]:
        set_xml_value(payload, user_ids, version=self.protocol.version)
    return payload

Inherited members

class GetEvents (*args, **kwargs)
Expand source code
class GetEvents(EWSAccountService):
    """MSDN:
    https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/getevents-operation
    """

    SERVICE_NAME = 'GetEvents'
    prefer_affinity = True

    def call(self, subscription_id, watermark):
        return self._elems_to_objs(self._get_elements(payload=self.get_payload(
                subscription_id=subscription_id, watermark=watermark,
        )))

    def _elems_to_objs(self, elems):
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            yield Notification.from_xml(elem=elem, account=None)

    @classmethod
    def _get_elements_in_container(cls, container):
        return container.findall(Notification.response_tag())

    def get_payload(self, subscription_id, watermark):
        getstreamingevents = create_element('m:%s' % self.SERVICE_NAME)
        add_xml_child(getstreamingevents, 'm:SubscriptionId', subscription_id)
        add_xml_child(getstreamingevents, 'm:Watermark', watermark)
        return getstreamingevents

Ancestors

Class variables

var SERVICE_NAME
var prefer_affinity

Methods

def call(self, subscription_id, watermark)
Expand source code
def call(self, subscription_id, watermark):
    return self._elems_to_objs(self._get_elements(payload=self.get_payload(
            subscription_id=subscription_id, watermark=watermark,
    )))
def get_payload(self, subscription_id, watermark)
Expand source code
def get_payload(self, subscription_id, watermark):
    getstreamingevents = create_element('m:%s' % self.SERVICE_NAME)
    add_xml_child(getstreamingevents, 'm:SubscriptionId', subscription_id)
    add_xml_child(getstreamingevents, 'm:Watermark', watermark)
    return getstreamingevents

Inherited members

class GetFolder (*args, **kwargs)
Expand source code
class GetFolder(EWSAccountService):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/getfolder-operation"""

    SERVICE_NAME = 'GetFolder'
    element_container_name = '{%s}Folders' % MNS
    ERRORS_TO_CATCH_IN_RESPONSE = EWSAccountService.ERRORS_TO_CATCH_IN_RESPONSE + (
        ErrorFolderNotFound, ErrorNoPublicFolderReplicaAvailable, ErrorInvalidOperation,
    )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.folders = []  # A hack to communicate parsing args to _elems_to_objs()

    def call(self, folders, additional_fields, shape):
        """Take a folder ID and returns the full information for that folder.

        :param folders: a list of Folder objects
        :param additional_fields: the extra fields that should be returned with the folder, as FieldPath objects
        :param shape: The set of attributes to return

        :return: XML elements for the folders, in stable order
        """
        # We can't easily find the correct folder class from the returned XML. Instead, return objects with the same
        # class as the folder instance it was requested with.
        self.folders = list(folders)  # Convert to a list, in case 'folders' is a generator. We're iterating twice.
        return self._elems_to_objs(self._chunked_get_elements(
            self.get_payload,
            items=self.folders,
            additional_fields=additional_fields,
            shape=shape,
        ))

    def _elems_to_objs(self, elems):
        for folder, elem in zip(self.folders, elems):
            if isinstance(elem, Exception):
                yield elem
                continue
            yield parse_folder_elem(elem=elem, folder=folder, account=self.account)

    def get_payload(self, folders, additional_fields, shape):
        getfolder = create_element('m:%s' % self.SERVICE_NAME)
        foldershape = create_shape_element(
            tag='m:FolderShape', shape=shape, additional_fields=additional_fields, version=self.account.version
        )
        getfolder.append(foldershape)
        folder_ids = create_folder_ids_element(tag='m:FolderIds', folders=folders, version=self.account.version)
        getfolder.append(folder_ids)
        return getfolder

Ancestors

Class variables

var ERRORS_TO_CATCH_IN_RESPONSE
var SERVICE_NAME
var element_container_name

Methods

def call(self, folders, additional_fields, shape)

Take a folder ID and returns the full information for that folder.

:param folders: a list of Folder objects :param additional_fields: the extra fields that should be returned with the folder, as FieldPath objects :param shape: The set of attributes to return

:return: XML elements for the folders, in stable order

Expand source code
def call(self, folders, additional_fields, shape):
    """Take a folder ID and returns the full information for that folder.

    :param folders: a list of Folder objects
    :param additional_fields: the extra fields that should be returned with the folder, as FieldPath objects
    :param shape: The set of attributes to return

    :return: XML elements for the folders, in stable order
    """
    # We can't easily find the correct folder class from the returned XML. Instead, return objects with the same
    # class as the folder instance it was requested with.
    self.folders = list(folders)  # Convert to a list, in case 'folders' is a generator. We're iterating twice.
    return self._elems_to_objs(self._chunked_get_elements(
        self.get_payload,
        items=self.folders,
        additional_fields=additional_fields,
        shape=shape,
    ))
def get_payload(self, folders, additional_fields, shape)
Expand source code
def get_payload(self, folders, additional_fields, shape):
    getfolder = create_element('m:%s' % self.SERVICE_NAME)
    foldershape = create_shape_element(
        tag='m:FolderShape', shape=shape, additional_fields=additional_fields, version=self.account.version
    )
    getfolder.append(foldershape)
    folder_ids = create_folder_ids_element(tag='m:FolderIds', folders=folders, version=self.account.version)
    getfolder.append(folder_ids)
    return getfolder

Inherited members

class GetItem (*args, **kwargs)
Expand source code
class GetItem(EWSAccountService):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/getitem-operation"""

    SERVICE_NAME = 'GetItem'
    element_container_name = '{%s}Items' % MNS

    def call(self, items, additional_fields, shape):
        """Return all items in an account that correspond to a list of ID's, in stable order.

        :param items: a list of (id, changekey) tuples or Item objects
        :param additional_fields: the extra fields that should be returned with the item, as FieldPath objects
        :param shape: The shape of returned objects

        :return: XML elements for the items, in stable order
        """
        return self._elems_to_objs(self._chunked_get_elements(
            self.get_payload, items=items, additional_fields=additional_fields, shape=shape,
        ))

    def _elems_to_objs(self, elems):
        from ..folders.base import BaseFolder
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            yield BaseFolder.item_model_from_tag(elem.tag).from_xml(elem=elem, account=self.account)

    def get_payload(self, items, additional_fields, shape):
        getitem = create_element('m:%s' % self.SERVICE_NAME)
        itemshape = create_shape_element(
            tag='m:ItemShape', shape=shape, additional_fields=additional_fields, version=self.account.version
        )
        getitem.append(itemshape)
        item_ids = create_item_ids_element(items=items, version=self.account.version)
        getitem.append(item_ids)
        return getitem

Ancestors

Class variables

var SERVICE_NAME
var element_container_name

Methods

def call(self, items, additional_fields, shape)

Return all items in an account that correspond to a list of ID's, in stable order.

:param items: a list of (id, changekey) tuples or Item objects :param additional_fields: the extra fields that should be returned with the item, as FieldPath objects :param shape: The shape of returned objects

:return: XML elements for the items, in stable order

Expand source code
def call(self, items, additional_fields, shape):
    """Return all items in an account that correspond to a list of ID's, in stable order.

    :param items: a list of (id, changekey) tuples or Item objects
    :param additional_fields: the extra fields that should be returned with the item, as FieldPath objects
    :param shape: The shape of returned objects

    :return: XML elements for the items, in stable order
    """
    return self._elems_to_objs(self._chunked_get_elements(
        self.get_payload, items=items, additional_fields=additional_fields, shape=shape,
    ))
def get_payload(self, items, additional_fields, shape)
Expand source code
def get_payload(self, items, additional_fields, shape):
    getitem = create_element('m:%s' % self.SERVICE_NAME)
    itemshape = create_shape_element(
        tag='m:ItemShape', shape=shape, additional_fields=additional_fields, version=self.account.version
    )
    getitem.append(itemshape)
    item_ids = create_item_ids_element(items=items, version=self.account.version)
    getitem.append(item_ids)
    return getitem

Inherited members

class GetMailTips (protocol, chunk_size=None, timeout=None)
Expand source code
class GetMailTips(EWSService):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/getmailtips-operation"""

    SERVICE_NAME = 'GetMailTips'

    def call(self, sending_as, recipients, mail_tips_requested):
        return self._elems_to_objs(self._chunked_get_elements(
            self.get_payload,
            items=recipients,
            sending_as=sending_as,
            mail_tips_requested=mail_tips_requested,
        ))

    def _elems_to_objs(self, elems):
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            yield MailTips.from_xml(elem=elem, account=None)

    def get_payload(self, recipients, sending_as,  mail_tips_requested):
        payload = create_element('m:%s' % self.SERVICE_NAME)
        set_xml_value(payload, sending_as, version=self.protocol.version)

        recipients_elem = create_element('m:Recipients')
        for recipient in recipients:
            set_xml_value(recipients_elem, recipient, version=self.protocol.version)
        if not len(recipients_elem):
            raise ValueError('"recipients" must not be empty')
        payload.append(recipients_elem)

        if mail_tips_requested:
            set_xml_value(payload, mail_tips_requested, version=self.protocol.version)
        return payload

    def _get_elements_in_response(self, response):
        for msg in response:
            yield self._get_element_container(message=msg, name=MailTips.response_tag())

    @classmethod
    def _response_message_tag(cls):
        return '{%s}MailTipsResponseMessageType' % MNS

Ancestors

Class variables

var SERVICE_NAME

Methods

def call(self, sending_as, recipients, mail_tips_requested)
Expand source code
def call(self, sending_as, recipients, mail_tips_requested):
    return self._elems_to_objs(self._chunked_get_elements(
        self.get_payload,
        items=recipients,
        sending_as=sending_as,
        mail_tips_requested=mail_tips_requested,
    ))
def get_payload(self, recipients, sending_as, mail_tips_requested)
Expand source code
def get_payload(self, recipients, sending_as,  mail_tips_requested):
    payload = create_element('m:%s' % self.SERVICE_NAME)
    set_xml_value(payload, sending_as, version=self.protocol.version)

    recipients_elem = create_element('m:Recipients')
    for recipient in recipients:
        set_xml_value(recipients_elem, recipient, version=self.protocol.version)
    if not len(recipients_elem):
        raise ValueError('"recipients" must not be empty')
    payload.append(recipients_elem)

    if mail_tips_requested:
        set_xml_value(payload, mail_tips_requested, version=self.protocol.version)
    return payload

Inherited members

class GetPersona (*args, **kwargs)
Expand source code
class GetPersona(EWSAccountService):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/getpersona-operation"""

    SERVICE_NAME = 'GetPersona'

    def call(self, persona):
        return self._elems_to_objs(self._get_elements(payload=self.get_payload(persona=persona)))

    def _elems_to_objs(self, elems):
        from ..items import Persona
        elements = list(elems)
        if len(elements) != 1:
            raise ValueError('Expected exactly one element in response')
        elem = elements[0]
        if isinstance(elem, Exception):
            raise elem
        return Persona.from_xml(elem=elem, account=None)

    def get_payload(self, persona):
        version = self.protocol.version
        payload = create_element('m:%s' % self.SERVICE_NAME)
        set_xml_value(payload, to_item_id(persona, PersonaId, version=version), version=version)
        return payload

    @classmethod
    def _get_elements_in_container(cls, container):
        from ..items import Persona
        return container.findall('{%s}%s' % (MNS, Persona.ELEMENT_NAME))

    @classmethod
    def _response_tag(cls):
        return '{%s}%sResponseMessage' % (MNS, cls.SERVICE_NAME)

Ancestors

Class variables

var SERVICE_NAME

Methods

def call(self, persona)
Expand source code
def call(self, persona):
    return self._elems_to_objs(self._get_elements(payload=self.get_payload(persona=persona)))
def get_payload(self, persona)
Expand source code
def get_payload(self, persona):
    version = self.protocol.version
    payload = create_element('m:%s' % self.SERVICE_NAME)
    set_xml_value(payload, to_item_id(persona, PersonaId, version=version), version=version)
    return payload

Inherited members

class GetRoomLists (protocol, chunk_size=None, timeout=None)
Expand source code
class GetRoomLists(EWSService):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/getroomlists-operation"""

    SERVICE_NAME = 'GetRoomLists'
    element_container_name = '{%s}RoomLists' % MNS
    supported_from = EXCHANGE_2010

    def call(self):
        return self._elems_to_objs(self._get_elements(payload=self.get_payload()))

    def _elems_to_objs(self, elems):
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            yield RoomList.from_xml(elem=elem, account=None)

    def get_payload(self):
        return create_element('m:%s' % self.SERVICE_NAME)

Ancestors

Class variables

var SERVICE_NAME
var element_container_name
var supported_from

Methods

def call(self)
Expand source code
def call(self):
    return self._elems_to_objs(self._get_elements(payload=self.get_payload()))
def get_payload(self)
Expand source code
def get_payload(self):
    return create_element('m:%s' % self.SERVICE_NAME)

Inherited members

class GetRooms (protocol, chunk_size=None, timeout=None)
Expand source code
class GetRooms(EWSService):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/getrooms-operation"""

    SERVICE_NAME = 'GetRooms'
    element_container_name = '{%s}Rooms' % MNS
    supported_from = EXCHANGE_2010

    def call(self, roomlist):
        return self._elems_to_objs(self._get_elements(payload=self.get_payload(roomlist=roomlist)))

    def _elems_to_objs(self, elems):
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            yield Room.from_xml(elem=elem, account=None)

    def get_payload(self, roomlist):
        getrooms = create_element('m:%s' % self.SERVICE_NAME)
        set_xml_value(getrooms, roomlist, version=self.protocol.version)
        return getrooms

Ancestors

Class variables

var SERVICE_NAME
var element_container_name
var supported_from

Methods

def call(self, roomlist)
Expand source code
def call(self, roomlist):
    return self._elems_to_objs(self._get_elements(payload=self.get_payload(roomlist=roomlist)))
def get_payload(self, roomlist)
Expand source code
def get_payload(self, roomlist):
    getrooms = create_element('m:%s' % self.SERVICE_NAME)
    set_xml_value(getrooms, roomlist, version=self.protocol.version)
    return getrooms

Inherited members

class GetSearchableMailboxes (protocol, chunk_size=None, timeout=None)
Expand source code
class GetSearchableMailboxes(EWSService):
    """MSDN:
    https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/getsearchablemailboxes-operation
    """

    SERVICE_NAME = 'GetSearchableMailboxes'
    element_container_name = '{%s}SearchableMailboxes' % MNS
    failed_mailboxes_container_name = '{%s}FailedMailboxes' % MNS
    supported_from = EXCHANGE_2013

    def call(self, search_filter, expand_group_membership):
        return self._elems_to_objs(self._get_elements(payload=self.get_payload(
                search_filter=search_filter,
                expand_group_membership=expand_group_membership,
        )))

    def _elems_to_objs(self, elems):
        cls_map = {cls.response_tag(): cls for cls in (SearchableMailbox, FailedMailbox)}
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            yield cls_map[elem.tag].from_xml(elem=elem, account=None)

    def get_payload(self, search_filter, expand_group_membership):
        payload = create_element('m:%s' % self.SERVICE_NAME)
        if search_filter:
            add_xml_child(payload, 'm:SearchFilter', search_filter)
        if expand_group_membership is not None:
            add_xml_child(payload, 'm:ExpandGroupMembership', 'true' if expand_group_membership else 'false')
        return payload

    def _get_elements_in_response(self, response):
        for msg in response:
            for container_name in (self.element_container_name, self.failed_mailboxes_container_name):
                try:
                    container_or_exc = self._get_element_container(message=msg, name=container_name)
                except MalformedResponseError:
                    # Responses may contain no failed mailboxes. _get_element_container() does not accept this.
                    if container_name == self.failed_mailboxes_container_name:
                        continue
                    raise
                if isinstance(container_or_exc, (bool, Exception)):
                    yield container_or_exc
                    continue
                yield from self._get_elements_in_container(container=container_or_exc)

Ancestors

Class variables

var SERVICE_NAME
var element_container_name
var failed_mailboxes_container_name
var supported_from

Methods

def call(self, search_filter, expand_group_membership)
Expand source code
def call(self, search_filter, expand_group_membership):
    return self._elems_to_objs(self._get_elements(payload=self.get_payload(
            search_filter=search_filter,
            expand_group_membership=expand_group_membership,
    )))
def get_payload(self, search_filter, expand_group_membership)
Expand source code
def get_payload(self, search_filter, expand_group_membership):
    payload = create_element('m:%s' % self.SERVICE_NAME)
    if search_filter:
        add_xml_child(payload, 'm:SearchFilter', search_filter)
    if expand_group_membership is not None:
        add_xml_child(payload, 'm:ExpandGroupMembership', 'true' if expand_group_membership else 'false')
    return payload

Inherited members

class GetServerTimeZones (protocol, chunk_size=None, timeout=None)
Expand source code
class GetServerTimeZones(EWSService):
    """MSDN:
    https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/getservertimezones-operation
    """

    SERVICE_NAME = 'GetServerTimeZones'
    element_container_name = '{%s}TimeZoneDefinitions' % MNS
    supported_from = EXCHANGE_2010

    def call(self, timezones=None, return_full_timezone_data=False):
        return self._elems_to_objs(self._get_elements(payload=self.get_payload(
            timezones=timezones,
            return_full_timezone_data=return_full_timezone_data
        )))

    def get_payload(self, timezones, return_full_timezone_data):
        payload = create_element(
            'm:%s' % self.SERVICE_NAME,
            attrs=dict(ReturnFullTimeZoneData='true' if return_full_timezone_data else 'false'),
        )
        if timezones is not None:
            is_empty, timezones = peek(timezones)
            if not is_empty:
                tz_ids = create_element('m:Ids')
                for timezone in timezones:
                    tz_id = set_xml_value(create_element('t:Id'), timezone.ms_id, version=self.protocol.version)
                    tz_ids.append(tz_id)
                payload.append(tz_ids)
        return payload

    def _elems_to_objs(self, elems):
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            tz_id = elem.get('Id')
            tz_name = elem.get('Name')
            tz_periods = self._get_periods(elem)
            tz_transitions_groups = self._get_transitions_groups(elem)
            tz_transitions = self._get_transitions(elem)
            yield tz_id, tz_name, tz_periods, tz_transitions, tz_transitions_groups

    @staticmethod
    def _get_periods(timezonedef):
        tz_periods = {}
        periods = timezonedef.find('{%s}Periods' % TNS)
        for period in periods.findall('{%s}Period' % TNS):
            # Convert e.g. "trule:Microsoft/Registry/W. Europe Standard Time/2006-Daylight" to (2006, 'Daylight')
            p_year, p_type = period.get('Id').rsplit('/', 1)[1].split('-')
            tz_periods[(int(p_year), p_type)] = dict(
                name=period.get('Name'),
                bias=xml_text_to_value(period.get('Bias'), datetime.timedelta)
            )
        return tz_periods

    @staticmethod
    def _get_transitions_groups(timezonedef):
        tz_transitions_groups = {}
        transitiongroups = timezonedef.find('{%s}TransitionsGroups' % TNS)
        if transitiongroups is not None:
            for transitiongroup in transitiongroups.findall('{%s}TransitionsGroup' % TNS):
                tg_id = int(transitiongroup.get('Id'))
                tz_transitions_groups[tg_id] = []
                for transition in transitiongroup.findall('{%s}Transition' % TNS):
                    # Apply same conversion to To as for period IDs
                    to_year, to_type = transition.find('{%s}To' % TNS).text.rsplit('/', 1)[1].split('-')
                    tz_transitions_groups[tg_id].append(dict(
                        to=(int(to_year), to_type),
                    ))
                for transition in transitiongroup.findall('{%s}RecurringDayTransition' % TNS):
                    # Apply same conversion to To as for period IDs
                    to_year, to_type = transition.find('{%s}To' % TNS).text.rsplit('/', 1)[1].split('-')
                    occurrence = xml_text_to_value(transition.find('{%s}Occurrence' % TNS).text, int)
                    if occurrence == -1:
                        # See TimeZoneTransition.from_xml()
                        occurrence = 5
                    tz_transitions_groups[tg_id].append(dict(
                        to=(int(to_year), to_type),
                        offset=xml_text_to_value(transition.find('{%s}TimeOffset' % TNS).text, datetime.timedelta),
                        iso_month=xml_text_to_value(transition.find('{%s}Month' % TNS).text, int),
                        iso_weekday=WEEKDAY_NAMES.index(transition.find('{%s}DayOfWeek' % TNS).text) + 1,
                        occurrence=occurrence,
                    ))
        return tz_transitions_groups

    @staticmethod
    def _get_transitions(timezonedef):
        tz_transitions = {}
        transitions = timezonedef.find('{%s}Transitions' % TNS)
        if transitions is not None:
            for transition in transitions.findall('{%s}Transition' % TNS):
                to = transition.find('{%s}To' % TNS)
                if to.get('Kind') != 'Group':
                    raise ValueError('Unexpected "Kind" XML attr: %s' % to.get('Kind'))
                tg_id = xml_text_to_value(to.text, int)
                tz_transitions[tg_id] = None
            for transition in transitions.findall('{%s}AbsoluteDateTransition' % TNS):
                to = transition.find('{%s}To' % TNS)
                if to.get('Kind') != 'Group':
                    raise ValueError('Unexpected "Kind" XML attr: %s' % to.get('Kind'))
                tg_id = xml_text_to_value(to.text, int)
                try:
                    t_date = xml_text_to_value(transition.find('{%s}DateTime' % TNS).text, EWSDateTime).date()
                except NaiveDateTimeNotAllowed as e:
                    # We encountered a naive datetime. Don't worry. we just need the date
                    t_date = e.local_dt.date()
                tz_transitions[tg_id] = t_date
        return tz_transitions

Ancestors

Class variables

var SERVICE_NAME
var element_container_name
var supported_from

Methods

def call(self, timezones=None, return_full_timezone_data=False)
Expand source code
def call(self, timezones=None, return_full_timezone_data=False):
    return self._elems_to_objs(self._get_elements(payload=self.get_payload(
        timezones=timezones,
        return_full_timezone_data=return_full_timezone_data
    )))
def get_payload(self, timezones, return_full_timezone_data)
Expand source code
def get_payload(self, timezones, return_full_timezone_data):
    payload = create_element(
        'm:%s' % self.SERVICE_NAME,
        attrs=dict(ReturnFullTimeZoneData='true' if return_full_timezone_data else 'false'),
    )
    if timezones is not None:
        is_empty, timezones = peek(timezones)
        if not is_empty:
            tz_ids = create_element('m:Ids')
            for timezone in timezones:
                tz_id = set_xml_value(create_element('t:Id'), timezone.ms_id, version=self.protocol.version)
                tz_ids.append(tz_id)
            payload.append(tz_ids)
    return payload

Inherited members

class GetStreamingEvents (*args, **kwargs)
Expand source code
class GetStreamingEvents(EWSAccountService):
    """MSDN:
    https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/getstreamingevents-operation
    """

    SERVICE_NAME = 'GetStreamingEvents'
    element_container_name = '{%s}Notifications' % MNS
    streaming = True
    prefer_affinity = True

    # Connection status values
    OK = 'OK'
    CLOSED = 'Closed'

    def __init__(self, *args, **kwargs):
        # These values are set each time call() is consumed
        self.connection_status = None
        self.error_subscription_ids = []
        super().__init__(*args, **kwargs)

    def call(self, subscription_ids, connection_timeout):
        if connection_timeout < 1:
            raise ValueError("'connection_timeout' must be a positive integer")
        return self._elems_to_objs(self._get_elements(payload=self.get_payload(
                subscription_ids=subscription_ids, connection_timeout=connection_timeout,
        )))

    def _elems_to_objs(self, elems):
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            yield Notification.from_xml(elem=elem, account=None)

    @classmethod
    def _get_soap_parts(cls, response, **parse_opts):
        # Pass the response unaltered. We want to use our custom document yielder
        return None, response

    def _get_soap_messages(self, body, **parse_opts):
        # 'body' is actually the raw response passed on by '_get_soap_parts'. We want to continuously read the content,
        # looking for complete XML documents. When we have a full document, we want to parse it as if it was a normal
        # XML response.
        r = body
        for i, doc in enumerate(DocumentYielder(r.iter_content()), start=1):
            xml_log.debug('''Response XML (docs received: %(i)s): %(xml_response)s''', dict(i=i, xml_response=doc))
            response = DummyResponse(url=None, headers=None, request_headers=None, content=doc)
            try:
                _, body = super()._get_soap_parts(response=response, **parse_opts)
            except Exception:
                r.close()  # Release memory
                raise
            # TODO: We're skipping ._update_api_version() here because we don't have access to the 'api_version' used.
            # TODO: We should be doing a lot of error handling for ._get_soap_messages().
            yield from super()._get_soap_messages(body=body, **parse_opts)
            if self.connection_status == self.CLOSED:
                # Don't wait for the TCP connection to timeout
                break

    def _get_element_container(self, message, name=None):
        error_ids_elem = message.find('{%s}ErrorSubscriptionIds' % MNS)
        if error_ids_elem is not None:
            self.error_subscription_ids = get_xml_attrs(error_ids_elem, '{%s}ErrorSubscriptionId' % MNS)
            log.debug('These subscription IDs are invalid: %s', self.error_subscription_ids)
        self.connection_status = get_xml_attr(message, '{%s}ConnectionStatus' % MNS)  # Either 'OK' or 'Closed'
        log.debug('Connection status is: %s', self.connection_status)
        # Upstream expects to find a 'name' tag but our response does not always have it. Return an empty element.
        if message.find(name) is None:
            return []
        return super()._get_element_container(message=message, name=name)

    def get_payload(self, subscription_ids, connection_timeout):
        getstreamingevents = create_element('m:%s' % self.SERVICE_NAME)
        subscriptions_elem = create_element('m:SubscriptionIds')
        for subscription_id in subscription_ids:
            add_xml_child(subscriptions_elem, 't:SubscriptionId', subscription_id)
        if not len(subscriptions_elem):
            raise ValueError('"subscription_ids" must not be empty')

        getstreamingevents.append(subscriptions_elem)
        add_xml_child(getstreamingevents, 'm:ConnectionTimeout', connection_timeout)
        return getstreamingevents

Ancestors

Class variables

var CLOSED
var OK
var SERVICE_NAME
var element_container_name
var prefer_affinity
var streaming

Methods

def call(self, subscription_ids, connection_timeout)
Expand source code
def call(self, subscription_ids, connection_timeout):
    if connection_timeout < 1:
        raise ValueError("'connection_timeout' must be a positive integer")
    return self._elems_to_objs(self._get_elements(payload=self.get_payload(
            subscription_ids=subscription_ids, connection_timeout=connection_timeout,
    )))
def get_payload(self, subscription_ids, connection_timeout)
Expand source code
def get_payload(self, subscription_ids, connection_timeout):
    getstreamingevents = create_element('m:%s' % self.SERVICE_NAME)
    subscriptions_elem = create_element('m:SubscriptionIds')
    for subscription_id in subscription_ids:
        add_xml_child(subscriptions_elem, 't:SubscriptionId', subscription_id)
    if not len(subscriptions_elem):
        raise ValueError('"subscription_ids" must not be empty')

    getstreamingevents.append(subscriptions_elem)
    add_xml_child(getstreamingevents, 'm:ConnectionTimeout', connection_timeout)
    return getstreamingevents

Inherited members

class GetUserAvailability (protocol, chunk_size=None, timeout=None)
Expand source code
class GetUserAvailability(EWSService):
    """Get detailed availability information for a list of users.
    MSDN:
    https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/getuseravailability-operation
    """

    SERVICE_NAME = 'GetUserAvailability'

    def call(self, timezone, mailbox_data, free_busy_view_options):
        # TODO: Also supports SuggestionsViewOptions, see
        #  https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/suggestionsviewoptions
        return self._elems_to_objs(self._get_elements(payload=self.get_payload(
            timezone=timezone,
            mailbox_data=mailbox_data,
            free_busy_view_options=free_busy_view_options
        )))

    def _elems_to_objs(self, elems):
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            yield FreeBusyView.from_xml(elem=elem, account=None)

    def get_payload(self, timezone, mailbox_data, free_busy_view_options):
        payload = create_element('m:%sRequest' % self.SERVICE_NAME)
        set_xml_value(payload, timezone, version=self.protocol.version)
        mailbox_data_array = create_element('m:MailboxDataArray')
        set_xml_value(mailbox_data_array, mailbox_data, version=self.protocol.version)
        payload.append(mailbox_data_array)
        set_xml_value(payload, free_busy_view_options, version=self.protocol.version)
        return payload

    @staticmethod
    def _response_messages_tag():
        return '{%s}FreeBusyResponseArray' % MNS

    @classmethod
    def _response_message_tag(cls):
        return '{%s}FreeBusyResponse' % MNS

    def _get_elements_in_response(self, response):
        for msg in response:
            # Just check the response code and raise errors
            self._get_element_container(message=msg.find('{%s}ResponseMessage' % MNS))
            yield from self._get_elements_in_container(container=msg)

    @classmethod
    def _get_elements_in_container(cls, container):
        return [container.find('{%s}FreeBusyView' % MNS)]

Ancestors

Class variables

var SERVICE_NAME

Methods

def call(self, timezone, mailbox_data, free_busy_view_options)
Expand source code
def call(self, timezone, mailbox_data, free_busy_view_options):
    # TODO: Also supports SuggestionsViewOptions, see
    #  https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/suggestionsviewoptions
    return self._elems_to_objs(self._get_elements(payload=self.get_payload(
        timezone=timezone,
        mailbox_data=mailbox_data,
        free_busy_view_options=free_busy_view_options
    )))
def get_payload(self, timezone, mailbox_data, free_busy_view_options)
Expand source code
def get_payload(self, timezone, mailbox_data, free_busy_view_options):
    payload = create_element('m:%sRequest' % self.SERVICE_NAME)
    set_xml_value(payload, timezone, version=self.protocol.version)
    mailbox_data_array = create_element('m:MailboxDataArray')
    set_xml_value(mailbox_data_array, mailbox_data, version=self.protocol.version)
    payload.append(mailbox_data_array)
    set_xml_value(payload, free_busy_view_options, version=self.protocol.version)
    return payload

Inherited members

class GetUserConfiguration (*args, **kwargs)
Expand source code
class GetUserConfiguration(EWSAccountService):
    """MSDN:
    https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/getuserconfiguration-operation
    """

    SERVICE_NAME = 'GetUserConfiguration'

    def call(self, user_configuration_name, properties):
        if properties not in PROPERTIES_CHOICES:
            raise ValueError("'properties' %r must be one of %s" % (properties, PROPERTIES_CHOICES))
        return self._elems_to_objs(self._get_elements(payload=self.get_payload(
                user_configuration_name=user_configuration_name, properties=properties
        )))

    def _elems_to_objs(self, elems):
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            yield UserConfiguration.from_xml(elem=elem, account=self.account)

    @classmethod
    def _get_elements_in_container(cls, container):
        return container.findall(UserConfiguration.response_tag())

    def get_payload(self, user_configuration_name, properties):
        getuserconfiguration = create_element('m:%s' % self.SERVICE_NAME)
        set_xml_value(getuserconfiguration, user_configuration_name, version=self.account.version)
        user_configuration_properties = create_element('m:UserConfigurationProperties')
        set_xml_value(user_configuration_properties, properties, version=self.account.version)
        getuserconfiguration.append(user_configuration_properties)
        return getuserconfiguration

Ancestors

Class variables

var SERVICE_NAME

Methods

def call(self, user_configuration_name, properties)
Expand source code
def call(self, user_configuration_name, properties):
    if properties not in PROPERTIES_CHOICES:
        raise ValueError("'properties' %r must be one of %s" % (properties, PROPERTIES_CHOICES))
    return self._elems_to_objs(self._get_elements(payload=self.get_payload(
            user_configuration_name=user_configuration_name, properties=properties
    )))
def get_payload(self, user_configuration_name, properties)
Expand source code
def get_payload(self, user_configuration_name, properties):
    getuserconfiguration = create_element('m:%s' % self.SERVICE_NAME)
    set_xml_value(getuserconfiguration, user_configuration_name, version=self.account.version)
    user_configuration_properties = create_element('m:UserConfigurationProperties')
    set_xml_value(user_configuration_properties, properties, version=self.account.version)
    getuserconfiguration.append(user_configuration_properties)
    return getuserconfiguration

Inherited members

class GetUserOofSettings (*args, **kwargs)
Expand source code
class GetUserOofSettings(EWSAccountService):
    """Get automatic reply settings for the specified mailbox.
    MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/getuseroofsettings-operation
    """

    SERVICE_NAME = 'GetUserOofSettings'
    element_container_name = '{%s}OofSettings' % TNS

    def call(self, mailbox):
        return self._elems_to_objs(self._get_elements(payload=self.get_payload(mailbox=mailbox)))

    def _elems_to_objs(self, elems):
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            yield OofSettings.from_xml(elem=elem, account=self.account)

    def get_payload(self, mailbox):
        payload = create_element('m:%sRequest' % self.SERVICE_NAME)
        return set_xml_value(payload, AvailabilityMailbox.from_mailbox(mailbox), version=self.account.version)

    @classmethod
    def _get_elements_in_container(cls, container):
        # This service only returns one result, directly in 'container'
        return [container]

    def _get_element_container(self, message, name=None):
        # This service returns the result container outside the response message
        super()._get_element_container(message=message.find(self._response_message_tag()), name=None)
        return message.find(name)

    @classmethod
    def _response_message_tag(cls):
        return '{%s}ResponseMessage' % MNS

Ancestors

Class variables

var SERVICE_NAME
var element_container_name

Methods

def call(self, mailbox)
Expand source code
def call(self, mailbox):
    return self._elems_to_objs(self._get_elements(payload=self.get_payload(mailbox=mailbox)))
def get_payload(self, mailbox)
Expand source code
def get_payload(self, mailbox):
    payload = create_element('m:%sRequest' % self.SERVICE_NAME)
    return set_xml_value(payload, AvailabilityMailbox.from_mailbox(mailbox), version=self.account.version)

Inherited members

class MarkAsJunk (*args, **kwargs)
Expand source code
class MarkAsJunk(EWSAccountService):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/markasjunk-operation"""

    SERVICE_NAME = 'MarkAsJunk'

    def call(self, items, is_junk, move_item):
        return self._elems_to_objs(
            self._chunked_get_elements(self.get_payload, items=items, is_junk=is_junk, move_item=move_item)
        )

    def _elems_to_objs(self, elems):
        for elem in elems:
            if isinstance(elem, (Exception, type(None))):
                yield elem
                continue
            yield MovedItemId.id_from_xml(elem)

    @classmethod
    def _get_elements_in_container(cls, container):
        return container.findall(MovedItemId.response_tag())

    def get_payload(self, items, is_junk, move_item):
        # Takes a list of items and returns either success or raises an error message
        mark_as_junk = create_element(
            'm:%s' % self.SERVICE_NAME,
            attrs=dict(IsJunk='true' if is_junk else 'false', MoveItem='true' if move_item else 'false')
        )
        item_ids = create_item_ids_element(items=items, version=self.account.version)
        mark_as_junk.append(item_ids)
        return mark_as_junk

Ancestors

Class variables

var SERVICE_NAME

Methods

def call(self, items, is_junk, move_item)
Expand source code
def call(self, items, is_junk, move_item):
    return self._elems_to_objs(
        self._chunked_get_elements(self.get_payload, items=items, is_junk=is_junk, move_item=move_item)
    )
def get_payload(self, items, is_junk, move_item)
Expand source code
def get_payload(self, items, is_junk, move_item):
    # Takes a list of items and returns either success or raises an error message
    mark_as_junk = create_element(
        'm:%s' % self.SERVICE_NAME,
        attrs=dict(IsJunk='true' if is_junk else 'false', MoveItem='true' if move_item else 'false')
    )
    item_ids = create_item_ids_element(items=items, version=self.account.version)
    mark_as_junk.append(item_ids)
    return mark_as_junk

Inherited members

class MoveFolder (*args, **kwargs)
Expand source code
class MoveFolder(EWSAccountService):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/movefolder-operation"""

    SERVICE_NAME = "MoveFolder"
    element_container_name = '{%s}Folders' % MNS

    def call(self, folders, to_folder):
        from ..folders import BaseFolder, FolderId
        if not isinstance(to_folder, (BaseFolder, FolderId)):
            raise ValueError("'to_folder' %r must be a Folder or FolderId instance" % to_folder)
        return self._elems_to_objs(self._chunked_get_elements(self.get_payload, items=folders, to_folder=to_folder))

    def _elems_to_objs(self, elems):
        from ..folders import FolderId
        for elem in elems:
            if isinstance(elem, (Exception, type(None))):
                yield elem
                continue
            yield FolderId.from_xml(elem=elem.find(FolderId.response_tag()), account=self.account)

    def get_payload(self, folders, to_folder):
        # Takes a list of folders and returns their new folder IDs
        movefolder = create_element('m:%s' % self.SERVICE_NAME)
        tofolderid = create_element('m:ToFolderId')
        set_xml_value(tofolderid, to_folder, version=self.account.version)
        movefolder.append(tofolderid)
        folder_ids = create_folder_ids_element(tag='m:FolderIds', folders=folders, version=self.account.version)
        movefolder.append(folder_ids)
        return movefolder

Ancestors

Class variables

var SERVICE_NAME
var element_container_name

Methods

def call(self, folders, to_folder)
Expand source code
def call(self, folders, to_folder):
    from ..folders import BaseFolder, FolderId
    if not isinstance(to_folder, (BaseFolder, FolderId)):
        raise ValueError("'to_folder' %r must be a Folder or FolderId instance" % to_folder)
    return self._elems_to_objs(self._chunked_get_elements(self.get_payload, items=folders, to_folder=to_folder))
def get_payload(self, folders, to_folder)
Expand source code
def get_payload(self, folders, to_folder):
    # Takes a list of folders and returns their new folder IDs
    movefolder = create_element('m:%s' % self.SERVICE_NAME)
    tofolderid = create_element('m:ToFolderId')
    set_xml_value(tofolderid, to_folder, version=self.account.version)
    movefolder.append(tofolderid)
    folder_ids = create_folder_ids_element(tag='m:FolderIds', folders=folders, version=self.account.version)
    movefolder.append(folder_ids)
    return movefolder

Inherited members

class MoveItem (*args, **kwargs)
Expand source code
class MoveItem(EWSAccountService):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/moveitem-operation"""

    SERVICE_NAME = 'MoveItem'
    element_container_name = '{%s}Items' % MNS

    def call(self, items, to_folder):
        from ..folders import BaseFolder, FolderId
        if not isinstance(to_folder, (BaseFolder, FolderId)):
            raise ValueError("'to_folder' %r must be a Folder or FolderId instance" % to_folder)
        return self._elems_to_objs(self._chunked_get_elements(self.get_payload, items=items, to_folder=to_folder))

    def _elems_to_objs(self, elems):
        from ..items import Item
        for elem in elems:
            if isinstance(elem, (Exception, type(None))):
                yield elem
                continue
            yield Item.id_from_xml(elem)

    def get_payload(self, items, to_folder):
        # Takes a list of items and returns their new item IDs
        moveitem = create_element('m:%s' % self.SERVICE_NAME)
        tofolderid = create_element('m:ToFolderId')
        set_xml_value(tofolderid, to_folder, version=self.account.version)
        moveitem.append(tofolderid)
        item_ids = create_item_ids_element(items=items, version=self.account.version)
        moveitem.append(item_ids)
        return moveitem

Ancestors

Subclasses

Class variables

var SERVICE_NAME
var element_container_name

Methods

def call(self, items, to_folder)
Expand source code
def call(self, items, to_folder):
    from ..folders import BaseFolder, FolderId
    if not isinstance(to_folder, (BaseFolder, FolderId)):
        raise ValueError("'to_folder' %r must be a Folder or FolderId instance" % to_folder)
    return self._elems_to_objs(self._chunked_get_elements(self.get_payload, items=items, to_folder=to_folder))
def get_payload(self, items, to_folder)
Expand source code
def get_payload(self, items, to_folder):
    # Takes a list of items and returns their new item IDs
    moveitem = create_element('m:%s' % self.SERVICE_NAME)
    tofolderid = create_element('m:ToFolderId')
    set_xml_value(tofolderid, to_folder, version=self.account.version)
    moveitem.append(tofolderid)
    item_ids = create_item_ids_element(items=items, version=self.account.version)
    moveitem.append(item_ids)
    return moveitem

Inherited members

class ResolveNames (*args, **kwargs)
Expand source code
class ResolveNames(EWSService):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/resolvenames-operation"""

    SERVICE_NAME = 'ResolveNames'
    element_container_name = '{%s}ResolutionSet' % MNS
    ERRORS_TO_CATCH_IN_RESPONSE = ErrorNameResolutionNoResults
    WARNINGS_TO_IGNORE_IN_RESPONSE = ErrorNameResolutionMultipleResults
    # Note: paging information is returned as attrs on the 'ResolutionSet' element, but this service does not
    # support the 'IndexedPageItemView' element, so it's not really a paging service. According to docs, at most
    # 100 candidates are returned for a lookup.
    supports_paging = False

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.return_full_contact_data = False  # A hack to communicate parsing args to _elems_to_objs()

    def call(self, unresolved_entries, parent_folders=None, return_full_contact_data=False, search_scope=None,
             contact_data_shape=None):
        from ..items import SHAPE_CHOICES, SEARCH_SCOPE_CHOICES
        if self.chunk_size > 100:
            log.warning(
                'Chunk size %s is dangerously high. %s supports returning at most 100 candidates for a lookup',
                self.chunk_size, self.SERVICE_NAME
            )
        if search_scope and search_scope not in SEARCH_SCOPE_CHOICES:
            raise ValueError("'search_scope' %s must be one if %s" % (search_scope, SEARCH_SCOPE_CHOICES))
        if contact_data_shape and contact_data_shape not in SHAPE_CHOICES:
            raise ValueError("'shape' %s must be one if %s" % (contact_data_shape, SHAPE_CHOICES))
        self.return_full_contact_data = return_full_contact_data
        return self._elems_to_objs(self._chunked_get_elements(
            self.get_payload,
            items=unresolved_entries,
            parent_folders=parent_folders,
            return_full_contact_data=return_full_contact_data,
            search_scope=search_scope,
            contact_data_shape=contact_data_shape,
        ))

    def _elems_to_objs(self, elems):
        from ..items import Contact
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            if self.return_full_contact_data:
                mailbox_elem = elem.find(Mailbox.response_tag())
                contact_elem = elem.find(Contact.response_tag())
                yield (
                    None if mailbox_elem is None else Mailbox.from_xml(elem=mailbox_elem, account=None),
                    None if contact_elem is None else Contact.from_xml(elem=contact_elem, account=None),
                )
            else:
                yield Mailbox.from_xml(elem=elem.find(Mailbox.response_tag()), account=None)

    def get_payload(self, unresolved_entries, parent_folders, return_full_contact_data, search_scope,
                    contact_data_shape):
        payload = create_element(
            'm:%s' % self.SERVICE_NAME,
            attrs=dict(ReturnFullContactData='true' if return_full_contact_data else 'false'),
        )
        if search_scope:
            payload.set('SearchScope', search_scope)
        if contact_data_shape:
            if self.protocol.version.build < EXCHANGE_2010_SP2:
                raise NotImplementedError(
                    "'contact_data_shape' is only supported for Exchange 2010 SP2 servers and later")
            payload.set('ContactDataShape', contact_data_shape)
        if parent_folders:
            parentfolderids = create_element('m:ParentFolderIds')
            set_xml_value(parentfolderids, parent_folders, version=self.protocol.version)
        for entry in unresolved_entries:
            add_xml_child(payload, 'm:UnresolvedEntry', entry)
        if not len(payload):
            raise ValueError('"unresolved_entries" must not be empty')
        return payload

Ancestors

Class variables

var ERRORS_TO_CATCH_IN_RESPONSE

Global error type within this module.

var SERVICE_NAME
var WARNINGS_TO_IGNORE_IN_RESPONSE

Global error type within this module.

var element_container_name
var supports_paging

Methods

def call(self, unresolved_entries, parent_folders=None, return_full_contact_data=False, search_scope=None, contact_data_shape=None)
Expand source code
def call(self, unresolved_entries, parent_folders=None, return_full_contact_data=False, search_scope=None,
         contact_data_shape=None):
    from ..items import SHAPE_CHOICES, SEARCH_SCOPE_CHOICES
    if self.chunk_size > 100:
        log.warning(
            'Chunk size %s is dangerously high. %s supports returning at most 100 candidates for a lookup',
            self.chunk_size, self.SERVICE_NAME
        )
    if search_scope and search_scope not in SEARCH_SCOPE_CHOICES:
        raise ValueError("'search_scope' %s must be one if %s" % (search_scope, SEARCH_SCOPE_CHOICES))
    if contact_data_shape and contact_data_shape not in SHAPE_CHOICES:
        raise ValueError("'shape' %s must be one if %s" % (contact_data_shape, SHAPE_CHOICES))
    self.return_full_contact_data = return_full_contact_data
    return self._elems_to_objs(self._chunked_get_elements(
        self.get_payload,
        items=unresolved_entries,
        parent_folders=parent_folders,
        return_full_contact_data=return_full_contact_data,
        search_scope=search_scope,
        contact_data_shape=contact_data_shape,
    ))
def get_payload(self, unresolved_entries, parent_folders, return_full_contact_data, search_scope, contact_data_shape)
Expand source code
def get_payload(self, unresolved_entries, parent_folders, return_full_contact_data, search_scope,
                contact_data_shape):
    payload = create_element(
        'm:%s' % self.SERVICE_NAME,
        attrs=dict(ReturnFullContactData='true' if return_full_contact_data else 'false'),
    )
    if search_scope:
        payload.set('SearchScope', search_scope)
    if contact_data_shape:
        if self.protocol.version.build < EXCHANGE_2010_SP2:
            raise NotImplementedError(
                "'contact_data_shape' is only supported for Exchange 2010 SP2 servers and later")
        payload.set('ContactDataShape', contact_data_shape)
    if parent_folders:
        parentfolderids = create_element('m:ParentFolderIds')
        set_xml_value(parentfolderids, parent_folders, version=self.protocol.version)
    for entry in unresolved_entries:
        add_xml_child(payload, 'm:UnresolvedEntry', entry)
    if not len(payload):
        raise ValueError('"unresolved_entries" must not be empty')
    return payload

Inherited members

class SendItem (*args, **kwargs)
Expand source code
class SendItem(EWSAccountService):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/senditem-operation"""

    SERVICE_NAME = 'SendItem'
    returns_elements = False

    def call(self, items, saved_item_folder):
        from ..folders import BaseFolder, FolderId
        if saved_item_folder and not isinstance(saved_item_folder, (BaseFolder, FolderId)):
            raise ValueError("'saved_item_folder' %r must be a Folder or FolderId instance" % saved_item_folder)
        return self._chunked_get_elements(self.get_payload, items=items, saved_item_folder=saved_item_folder)

    def get_payload(self, items, saved_item_folder):
        senditem = create_element(
            'm:%s' % self.SERVICE_NAME,
            attrs=dict(SaveItemToFolder='true' if saved_item_folder else 'false'),
        )
        item_ids = create_item_ids_element(items=items, version=self.account.version)
        senditem.append(item_ids)
        if saved_item_folder:
            saveditemfolderid = create_element('m:SavedItemFolderId')
            set_xml_value(saveditemfolderid, saved_item_folder, version=self.account.version)
            senditem.append(saveditemfolderid)
        return senditem

Ancestors

Class variables

var SERVICE_NAME
var returns_elements

Methods

def call(self, items, saved_item_folder)
Expand source code
def call(self, items, saved_item_folder):
    from ..folders import BaseFolder, FolderId
    if saved_item_folder and not isinstance(saved_item_folder, (BaseFolder, FolderId)):
        raise ValueError("'saved_item_folder' %r must be a Folder or FolderId instance" % saved_item_folder)
    return self._chunked_get_elements(self.get_payload, items=items, saved_item_folder=saved_item_folder)
def get_payload(self, items, saved_item_folder)
Expand source code
def get_payload(self, items, saved_item_folder):
    senditem = create_element(
        'm:%s' % self.SERVICE_NAME,
        attrs=dict(SaveItemToFolder='true' if saved_item_folder else 'false'),
    )
    item_ids = create_item_ids_element(items=items, version=self.account.version)
    senditem.append(item_ids)
    if saved_item_folder:
        saveditemfolderid = create_element('m:SavedItemFolderId')
        set_xml_value(saveditemfolderid, saved_item_folder, version=self.account.version)
        senditem.append(saveditemfolderid)
    return senditem

Inherited members

class SendNotification (protocol, chunk_size=None, timeout=None)

MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/sendnotification

This is not an actual EWS service you can call. We only use it to parse the XML body of push notifications.

Expand source code
class SendNotification(EWSService):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/sendnotification

    This is not an actual EWS service you can call. We only use it to parse the XML body of push notifications.
    """

    SERVICE_NAME = 'SendNotification'

    def call(self):
        raise NotImplementedError()

    def _elems_to_objs(self, elems):
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            yield Notification.from_xml(elem=elem, account=None)

    @classmethod
    def _response_tag(cls):
        """Return the name of the element containing the service response."""
        return '{%s}%s' % (MNS, cls.SERVICE_NAME)

    @classmethod
    def _get_elements_in_container(cls, container):
        return container.findall(Notification.response_tag())

Ancestors

Class variables

var SERVICE_NAME

Methods

def call(self)
Expand source code
def call(self):
    raise NotImplementedError()

Inherited members

class SetUserOofSettings (*args, **kwargs)
Expand source code
class SetUserOofSettings(EWSAccountService):
    """Set automatic replies for the specified mailbox.
    MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/setuseroofsettings-operation
    """

    SERVICE_NAME = 'SetUserOofSettings'
    returns_elements = False

    def call(self, oof_settings, mailbox):
        if not isinstance(oof_settings, OofSettings):
            raise ValueError("'oof_settings' %r must be an OofSettings instance" % oof_settings)
        if not isinstance(mailbox, Mailbox):
            raise ValueError("'mailbox' %r must be an Mailbox instance" % mailbox)
        return self._get_elements(payload=self.get_payload(oof_settings=oof_settings, mailbox=mailbox))

    def get_payload(self, oof_settings, mailbox):
        payload = create_element('m:%sRequest' % self.SERVICE_NAME)
        set_xml_value(payload, AvailabilityMailbox.from_mailbox(mailbox), version=self.account.version)
        set_xml_value(payload, oof_settings, version=self.account.version)
        return payload

    def _get_element_container(self, message, name=None):
        message = message.find(self._response_message_tag())
        return super()._get_element_container(message=message, name=name)

    @classmethod
    def _response_message_tag(cls):
        return '{%s}ResponseMessage' % MNS

Ancestors

Class variables

var SERVICE_NAME
var returns_elements

Methods

def call(self, oof_settings, mailbox)
Expand source code
def call(self, oof_settings, mailbox):
    if not isinstance(oof_settings, OofSettings):
        raise ValueError("'oof_settings' %r must be an OofSettings instance" % oof_settings)
    if not isinstance(mailbox, Mailbox):
        raise ValueError("'mailbox' %r must be an Mailbox instance" % mailbox)
    return self._get_elements(payload=self.get_payload(oof_settings=oof_settings, mailbox=mailbox))
def get_payload(self, oof_settings, mailbox)
Expand source code
def get_payload(self, oof_settings, mailbox):
    payload = create_element('m:%sRequest' % self.SERVICE_NAME)
    set_xml_value(payload, AvailabilityMailbox.from_mailbox(mailbox), version=self.account.version)
    set_xml_value(payload, oof_settings, version=self.account.version)
    return payload

Inherited members

class SubscribeToPull (*args, **kwargs)
Expand source code
class SubscribeToPull(Subscribe):
    subscription_request_elem_tag = 'm:PullSubscriptionRequest'

    def call(self, folders, event_types, watermark, timeout):
        yield from self._partial_call(
            payload_func=self.get_payload, folders=folders, event_types=event_types, timeout=timeout,
            watermark=watermark,
        )

    def get_payload(self, folders, event_types, watermark, timeout):
        subscribe = create_element('m:%s' % self.SERVICE_NAME)
        request_elem = self._partial_payload(folders=folders, event_types=event_types)
        if watermark:
            add_xml_child(request_elem, 'm:Watermark', watermark)
        add_xml_child(request_elem, 't:Timeout', timeout)  # In minutes
        subscribe.append(request_elem)
        return subscribe

Ancestors

Class variables

var subscription_request_elem_tag

Methods

def call(self, folders, event_types, watermark, timeout)
Expand source code
def call(self, folders, event_types, watermark, timeout):
    yield from self._partial_call(
        payload_func=self.get_payload, folders=folders, event_types=event_types, timeout=timeout,
        watermark=watermark,
    )
def get_payload(self, folders, event_types, watermark, timeout)
Expand source code
def get_payload(self, folders, event_types, watermark, timeout):
    subscribe = create_element('m:%s' % self.SERVICE_NAME)
    request_elem = self._partial_payload(folders=folders, event_types=event_types)
    if watermark:
        add_xml_child(request_elem, 'm:Watermark', watermark)
    add_xml_child(request_elem, 't:Timeout', timeout)  # In minutes
    subscribe.append(request_elem)
    return subscribe

Inherited members

class SubscribeToPush (*args, **kwargs)
Expand source code
class SubscribeToPush(Subscribe):
    subscription_request_elem_tag = 'm:PushSubscriptionRequest'

    def call(self, folders, event_types, watermark, status_frequency, url):
        yield from self._partial_call(
            payload_func=self.get_payload, folders=folders, event_types=event_types,
            status_frequency=status_frequency, url=url, watermark=watermark,
        )

    def get_payload(self, folders, event_types, watermark, status_frequency, url):
        subscribe = create_element('m:%s' % self.SERVICE_NAME)
        request_elem = self._partial_payload(folders=folders, event_types=event_types)
        if watermark:
            add_xml_child(request_elem, 'm:Watermark', watermark)
        add_xml_child(request_elem, 't:StatusFrequency', status_frequency)  # In minutes
        add_xml_child(request_elem, 't:URL', url)
        subscribe.append(request_elem)
        return subscribe

Ancestors

Class variables

var subscription_request_elem_tag

Methods

def call(self, folders, event_types, watermark, status_frequency, url)
Expand source code
def call(self, folders, event_types, watermark, status_frequency, url):
    yield from self._partial_call(
        payload_func=self.get_payload, folders=folders, event_types=event_types,
        status_frequency=status_frequency, url=url, watermark=watermark,
    )
def get_payload(self, folders, event_types, watermark, status_frequency, url)
Expand source code
def get_payload(self, folders, event_types, watermark, status_frequency, url):
    subscribe = create_element('m:%s' % self.SERVICE_NAME)
    request_elem = self._partial_payload(folders=folders, event_types=event_types)
    if watermark:
        add_xml_child(request_elem, 'm:Watermark', watermark)
    add_xml_child(request_elem, 't:StatusFrequency', status_frequency)  # In minutes
    add_xml_child(request_elem, 't:URL', url)
    subscribe.append(request_elem)
    return subscribe

Inherited members

class SubscribeToStreaming (*args, **kwargs)
Expand source code
class SubscribeToStreaming(Subscribe):
    subscription_request_elem_tag = 'm:StreamingSubscriptionRequest'

    def call(self, folders, event_types):
        yield from self._partial_call(payload_func=self.get_payload, folders=folders, event_types=event_types)

    def _elems_to_objs(self, elems):
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            yield elem.text

    @classmethod
    def _get_elements_in_container(cls, container):
        return [container.find('{%s}SubscriptionId' % MNS)]

    def get_payload(self, folders, event_types):
        subscribe = create_element('m:%s' % self.SERVICE_NAME)
        request_elem = self._partial_payload(folders=folders, event_types=event_types)
        subscribe.append(request_elem)
        return subscribe

Ancestors

Class variables

var subscription_request_elem_tag

Methods

def call(self, folders, event_types)
Expand source code
def call(self, folders, event_types):
    yield from self._partial_call(payload_func=self.get_payload, folders=folders, event_types=event_types)
def get_payload(self, folders, event_types)
Expand source code
def get_payload(self, folders, event_types):
    subscribe = create_element('m:%s' % self.SERVICE_NAME)
    request_elem = self._partial_payload(folders=folders, event_types=event_types)
    subscribe.append(request_elem)
    return subscribe

Inherited members

class SyncFolderHierarchy (*args, **kwargs)
Expand source code
class SyncFolderHierarchy(SyncFolder):
    """MSDN:
    https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/syncfolderhierarchy-operation
    """

    SERVICE_NAME = 'SyncFolderHierarchy'
    shape_tag = 'm:FolderShape'
    last_in_range_name = '{%s}IncludesLastFolderInRange' % MNS

    def call(self, folder, shape, additional_fields, sync_state):
        self.sync_state = sync_state
        change_types = self._change_types_map()
        for elem in self._get_elements(payload=self.get_payload(
                folder=folder,
                shape=shape,
                additional_fields=additional_fields,
                sync_state=sync_state,
        )):
            if isinstance(elem, Exception):
                yield elem
                continue
            change_type = change_types[elem.tag]
            if change_type == self.DELETE:
                folder = FolderId.from_xml(elem=elem.find(FolderId.response_tag()), account=self.account)
            else:
                # We can't find() the element because we don't know which tag to look for. The change element can
                # contain multiple folder types, each with their own tag.
                folder_elem = elem[0]
                folder = parse_folder_elem(elem=folder_elem, folder=folder, account=self.account)
            yield change_type, folder

    def get_payload(self, folder, shape, additional_fields, sync_state):
        return self._partial_get_payload(
            folder=folder, shape=shape, additional_fields=additional_fields, sync_state=sync_state
        )

Ancestors

Class variables

var SERVICE_NAME
var last_in_range_name
var shape_tag

Methods

def call(self, folder, shape, additional_fields, sync_state)
Expand source code
def call(self, folder, shape, additional_fields, sync_state):
    self.sync_state = sync_state
    change_types = self._change_types_map()
    for elem in self._get_elements(payload=self.get_payload(
            folder=folder,
            shape=shape,
            additional_fields=additional_fields,
            sync_state=sync_state,
    )):
        if isinstance(elem, Exception):
            yield elem
            continue
        change_type = change_types[elem.tag]
        if change_type == self.DELETE:
            folder = FolderId.from_xml(elem=elem.find(FolderId.response_tag()), account=self.account)
        else:
            # We can't find() the element because we don't know which tag to look for. The change element can
            # contain multiple folder types, each with their own tag.
            folder_elem = elem[0]
            folder = parse_folder_elem(elem=folder_elem, folder=folder, account=self.account)
        yield change_type, folder
def get_payload(self, folder, shape, additional_fields, sync_state)
Expand source code
def get_payload(self, folder, shape, additional_fields, sync_state):
    return self._partial_get_payload(
        folder=folder, shape=shape, additional_fields=additional_fields, sync_state=sync_state
    )

Inherited members

class SyncFolderItems (*args, **kwargs)
Expand source code
class SyncFolderItems(SyncFolder):
    """MSDN:
    https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/syncfolderitems-operation
    """

    SERVICE_NAME = 'SyncFolderItems'
    SYNC_SCOPES = {
        'NormalItems',
        'NormalAndAssociatedItems',
    }
    # Extra change type
    READ_FLAG_CHANGE = 'read_flag_change'
    CHANGE_TYPES = SyncFolder.CHANGE_TYPES + (READ_FLAG_CHANGE,)
    shape_tag = 'm:ItemShape'
    last_in_range_name = '{%s}IncludesLastItemInRange' % MNS

    def _change_types_map(self):
        res = super()._change_types_map()
        res['{%s}ReadFlagChange' % TNS] = self.READ_FLAG_CHANGE
        return res

    def call(self, folder, shape, additional_fields, sync_state, ignore, max_changes_returned, sync_scope):
        self.sync_state = sync_state
        if max_changes_returned is None:
            max_changes_returned = self.chunk_size
        if max_changes_returned <= 0:
            raise ValueError("'max_changes_returned' %s must be a positive integer" % max_changes_returned)
        if sync_scope is not None and sync_scope not in self.SYNC_SCOPES:
            raise ValueError("'sync_scope' %s must be one of %r" % (sync_scope, self.SYNC_SCOPES))
        return self._elems_to_objs(self._get_elements(payload=self.get_payload(
                folder=folder,
                shape=shape,
                additional_fields=additional_fields,
                sync_state=sync_state,
                ignore=ignore,
                max_changes_returned=max_changes_returned,
                sync_scope=sync_scope,
        )))

    def _elems_to_objs(self, elems):
        from ..folders.base import BaseFolder
        change_types = self._change_types_map()
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            change_type = change_types[elem.tag]
            if change_type == self.READ_FLAG_CHANGE:
                item = (
                    ItemId.from_xml(elem=elem.find(ItemId.response_tag()), account=self.account),
                    xml_text_to_value(elem.find('{%s}IsRead' % TNS).text, bool)
                )
            elif change_type == self.DELETE:
                item = ItemId.from_xml(elem=elem.find(ItemId.response_tag()), account=self.account)
            else:
                # We can't find() the element because we don't know which tag to look for. The change element can
                # contain multiple item types, each with their own tag.
                item_elem = elem[0]
                item = BaseFolder.item_model_from_tag(item_elem.tag).from_xml(elem=item_elem, account=self.account)
            yield change_type, item

    def get_payload(self, folder, shape, additional_fields, sync_state, ignore, max_changes_returned, sync_scope):
        syncfolderitems = self._partial_get_payload(
            folder=folder, shape=shape, additional_fields=additional_fields, sync_state=sync_state
        )
        is_empty, ignore = (True, None) if ignore is None else peek(ignore)
        if not is_empty:
            item_ids = create_item_ids_element(items=ignore, version=self.account.version, tag='m:Ignore')
            syncfolderitems.append(item_ids)
        add_xml_child(syncfolderitems, 'm:MaxChangesReturned', max_changes_returned)
        if sync_scope:
            add_xml_child(syncfolderitems, 'm:SyncScope', sync_scope)
        return syncfolderitems

Ancestors

Class variables

var CHANGE_TYPES
var READ_FLAG_CHANGE
var SERVICE_NAME
var SYNC_SCOPES
var last_in_range_name
var shape_tag

Methods

def call(self, folder, shape, additional_fields, sync_state, ignore, max_changes_returned, sync_scope)
Expand source code
def call(self, folder, shape, additional_fields, sync_state, ignore, max_changes_returned, sync_scope):
    self.sync_state = sync_state
    if max_changes_returned is None:
        max_changes_returned = self.chunk_size
    if max_changes_returned <= 0:
        raise ValueError("'max_changes_returned' %s must be a positive integer" % max_changes_returned)
    if sync_scope is not None and sync_scope not in self.SYNC_SCOPES:
        raise ValueError("'sync_scope' %s must be one of %r" % (sync_scope, self.SYNC_SCOPES))
    return self._elems_to_objs(self._get_elements(payload=self.get_payload(
            folder=folder,
            shape=shape,
            additional_fields=additional_fields,
            sync_state=sync_state,
            ignore=ignore,
            max_changes_returned=max_changes_returned,
            sync_scope=sync_scope,
    )))
def get_payload(self, folder, shape, additional_fields, sync_state, ignore, max_changes_returned, sync_scope)
Expand source code
def get_payload(self, folder, shape, additional_fields, sync_state, ignore, max_changes_returned, sync_scope):
    syncfolderitems = self._partial_get_payload(
        folder=folder, shape=shape, additional_fields=additional_fields, sync_state=sync_state
    )
    is_empty, ignore = (True, None) if ignore is None else peek(ignore)
    if not is_empty:
        item_ids = create_item_ids_element(items=ignore, version=self.account.version, tag='m:Ignore')
        syncfolderitems.append(item_ids)
    add_xml_child(syncfolderitems, 'm:MaxChangesReturned', max_changes_returned)
    if sync_scope:
        add_xml_child(syncfolderitems, 'm:SyncScope', sync_scope)
    return syncfolderitems

Inherited members

class Unsubscribe (*args, **kwargs)

Unsubscribing is only valid for pull and streaming notifications.

MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/unsubscribe-operation

Expand source code
class Unsubscribe(EWSAccountService):
    """Unsubscribing is only valid for pull and streaming notifications.

    MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/unsubscribe-operation
    """

    SERVICE_NAME = 'Unsubscribe'
    returns_elements = False

    def call(self, subscription_id):
        return self._get_elements(payload=self.get_payload(subscription_id=subscription_id))

    def get_payload(self, subscription_id):
        unsubscribe = create_element('m:%s' % self.SERVICE_NAME)
        add_xml_child(unsubscribe, 'm:SubscriptionId', subscription_id)
        return unsubscribe

Ancestors

Class variables

var SERVICE_NAME
var returns_elements

Methods

def call(self, subscription_id)
Expand source code
def call(self, subscription_id):
    return self._get_elements(payload=self.get_payload(subscription_id=subscription_id))
def get_payload(self, subscription_id)
Expand source code
def get_payload(self, subscription_id):
    unsubscribe = create_element('m:%s' % self.SERVICE_NAME)
    add_xml_child(unsubscribe, 'm:SubscriptionId', subscription_id)
    return unsubscribe

Inherited members

class UpdateFolder (*args, **kwargs)
Expand source code
class UpdateFolder(EWSAccountService):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/updatefolder-operation"""

    SERVICE_NAME = 'UpdateFolder'
    element_container_name = '{%s}Folders' % MNS

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.folders = []  # A hack to communicate parsing args to _elems_to_objs()

    def call(self, folders):
        # We can't easily find the correct folder class from the returned XML. Instead, return objects with the same
        # class as the folder instance it was requested with.
        self.folders = list(folders)  # Convert to a list, in case 'folders' is a generator. We're iterating twice.
        return self._elems_to_objs(self._chunked_get_elements(self.get_payload, items=self.folders))

    def _elems_to_objs(self, elems):
        for (folder, _), elem in zip(self.folders, elems):
            if isinstance(elem, Exception):
                yield elem
                continue
            yield parse_folder_elem(elem=elem, folder=folder, account=self.account)

    @staticmethod
    def _sort_fieldnames(folder_model, fieldnames):
        # Take a list of fieldnames and return the fields in the order they are mentioned in folder_model.FIELDS.
        # Loop over FIELDS and not supported_fields(). Upstream should make sure not to update a non-supported field.
        for f in folder_model.FIELDS:
            if f.name in fieldnames:
                yield f.name

    def _set_folder_elem(self, folder_model, field_path, value):
        setfolderfield = create_element('t:SetFolderField')
        set_xml_value(setfolderfield, field_path, version=self.account.version)
        folder = create_element(folder_model.request_tag())
        field_elem = field_path.field.to_xml(value, version=self.account.version)
        set_xml_value(folder, field_elem, version=self.account.version)
        setfolderfield.append(folder)
        return setfolderfield

    def _delete_folder_elem(self, field_path):
        deletefolderfield = create_element('t:DeleteFolderField')
        return set_xml_value(deletefolderfield, field_path, version=self.account.version)

    def _get_folder_update_elems(self, folder, fieldnames):
        folder_model = folder.__class__
        fieldnames_set = set(fieldnames)

        for fieldname in self._sort_fieldnames(folder_model=folder_model, fieldnames=fieldnames_set):
            field = folder_model.get_field_by_fieldname(fieldname)
            if field.is_read_only:
                raise ValueError('%s is a read-only field' % field.name)
            value = field.clean(getattr(folder, field.name), version=self.account.version)  # Make sure the value is OK

            if value is None or (field.is_list and not value):
                # A value of None or [] means we want to remove this field from the item
                if field.is_required or field.is_required_after_save:
                    raise ValueError('%s is a required field and may not be deleted' % field.name)
                for field_path in FieldPath(field=field).expand(version=self.account.version):
                    yield self._delete_folder_elem(field_path=field_path)
                continue

            yield self._set_folder_elem(folder_model=folder_model, field_path=FieldPath(field=field), value=value)

    def get_payload(self, folders):
        from ..folders import BaseFolder, FolderId
        updatefolder = create_element('m:%s' % self.SERVICE_NAME)
        folderchanges = create_element('m:FolderChanges')
        version = self.account.version
        for folder, fieldnames in folders:
            folderchange = create_element('t:FolderChange')
            if not isinstance(folder, (BaseFolder, FolderId)):
                folder = to_item_id(folder, FolderId, version=version)
            set_xml_value(folderchange, folder, version=version)
            updates = create_element('t:Updates')
            for elem in self._get_folder_update_elems(folder=folder, fieldnames=fieldnames):
                updates.append(elem)
            folderchange.append(updates)
            folderchanges.append(folderchange)
        if not len(folderchanges):
            raise ValueError('"folders" must not be empty')
        updatefolder.append(folderchanges)
        return updatefolder

Ancestors

Class variables

var SERVICE_NAME
var element_container_name

Methods

def call(self, folders)
Expand source code
def call(self, folders):
    # We can't easily find the correct folder class from the returned XML. Instead, return objects with the same
    # class as the folder instance it was requested with.
    self.folders = list(folders)  # Convert to a list, in case 'folders' is a generator. We're iterating twice.
    return self._elems_to_objs(self._chunked_get_elements(self.get_payload, items=self.folders))
def get_payload(self, folders)
Expand source code
def get_payload(self, folders):
    from ..folders import BaseFolder, FolderId
    updatefolder = create_element('m:%s' % self.SERVICE_NAME)
    folderchanges = create_element('m:FolderChanges')
    version = self.account.version
    for folder, fieldnames in folders:
        folderchange = create_element('t:FolderChange')
        if not isinstance(folder, (BaseFolder, FolderId)):
            folder = to_item_id(folder, FolderId, version=version)
        set_xml_value(folderchange, folder, version=version)
        updates = create_element('t:Updates')
        for elem in self._get_folder_update_elems(folder=folder, fieldnames=fieldnames):
            updates.append(elem)
        folderchange.append(updates)
        folderchanges.append(folderchange)
    if not len(folderchanges):
        raise ValueError('"folders" must not be empty')
    updatefolder.append(folderchanges)
    return updatefolder

Inherited members

class UpdateItem (*args, **kwargs)
Expand source code
class UpdateItem(EWSAccountService):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/updateitem-operation"""

    SERVICE_NAME = 'UpdateItem'
    element_container_name = '{%s}Items' % MNS

    def call(self, items, conflict_resolution, message_disposition, send_meeting_invitations_or_cancellations,
             suppress_read_receipts):
        from ..items import CONFLICT_RESOLUTION_CHOICES, MESSAGE_DISPOSITION_CHOICES, \
            SEND_MEETING_INVITATIONS_AND_CANCELLATIONS_CHOICES, SEND_ONLY
        if conflict_resolution not in CONFLICT_RESOLUTION_CHOICES:
            raise ValueError("'conflict_resolution' %s must be one of %s" % (
                conflict_resolution, CONFLICT_RESOLUTION_CHOICES
            ))
        if message_disposition not in MESSAGE_DISPOSITION_CHOICES:
            raise ValueError("'message_disposition' %s must be one of %s" % (
                message_disposition, MESSAGE_DISPOSITION_CHOICES
            ))
        if send_meeting_invitations_or_cancellations not in SEND_MEETING_INVITATIONS_AND_CANCELLATIONS_CHOICES:
            raise ValueError("'send_meeting_invitations_or_cancellations' %s must be one of %s" % (
                send_meeting_invitations_or_cancellations, SEND_MEETING_INVITATIONS_AND_CANCELLATIONS_CHOICES
            ))
        if suppress_read_receipts not in (True, False):
            raise ValueError("'suppress_read_receipts' %s must be True or False" % suppress_read_receipts)
        if message_disposition == SEND_ONLY:
            raise ValueError('Cannot send-only existing objects. Use SendItem service instead')
        return self._elems_to_objs(self._chunked_get_elements(
            self.get_payload,
            items=items,
            conflict_resolution=conflict_resolution,
            message_disposition=message_disposition,
            send_meeting_invitations_or_cancellations=send_meeting_invitations_or_cancellations,
            suppress_read_receipts=suppress_read_receipts,
        ))

    def _elems_to_objs(self, elems):
        from ..items import Item
        for elem in elems:
            if isinstance(elem, (Exception, type(None))):
                yield elem
                continue
            yield Item.id_from_xml(elem)

    def _delete_item_elem(self, field_path):
        deleteitemfield = create_element('t:DeleteItemField')
        return set_xml_value(deleteitemfield, field_path, version=self.account.version)

    def _set_item_elem(self, item_model, field_path, value):
        setitemfield = create_element('t:SetItemField')
        set_xml_value(setitemfield, field_path, version=self.account.version)
        item_elem = create_element(item_model.request_tag())
        field_elem = field_path.field.to_xml(value, version=self.account.version)
        set_xml_value(item_elem, field_elem, version=self.account.version)
        setitemfield.append(item_elem)
        return setitemfield

    @staticmethod
    def _sorted_fields(item_model, fieldnames):
        # Take a list of fieldnames and return the (unique) fields in the order they are mentioned in item_class.FIELDS.
        # Checks that all fieldnames are valid.
        unique_fieldnames = list(OrderedDict.fromkeys(fieldnames))  # Make field names unique ,but keep ordering
        # Loop over FIELDS and not supported_fields(). Upstream should make sure not to update a non-supported field.
        for f in item_model.FIELDS:
            if f.name in unique_fieldnames:
                unique_fieldnames.remove(f.name)
                yield f
        if unique_fieldnames:
            raise ValueError("Field name(s) %s are not valid for a '%s' item" % (
                ', '.join("'%s'" % f for f in unique_fieldnames), item_model.__name__))

    def _get_item_update_elems(self, item, fieldnames):
        from ..items import CalendarItem
        fieldnames_copy = list(fieldnames)

        if item.__class__ == CalendarItem:
            # For CalendarItem items where we update 'start' or 'end', we want to update internal timezone fields
            item.clean_timezone_fields(version=self.account.version)  # Possibly also sets timezone values
            for field_name in ('start', 'end'):
                if field_name in fieldnames_copy:
                    tz_field_name = item.tz_field_for_field_name(field_name).name
                    if tz_field_name not in fieldnames_copy:
                        fieldnames_copy.append(tz_field_name)

        for field in self._sorted_fields(item_model=item.__class__, fieldnames=fieldnames_copy):
            if field.is_read_only:
                raise ValueError('%s is a read-only field' % field.name)
            value = self._get_item_value(item, field)
            if value is None or (field.is_list and not value):
                # A value of None or [] means we want to remove this field from the item
                yield from self._get_delete_item_elems(field=field)
            else:
                yield from self._get_set_item_elems(item_model=item.__class__, field=field, value=value)

    def _get_item_value(self, item, field):
        from ..items import CalendarItem
        value = field.clean(getattr(item, field.name), version=self.account.version)  # Make sure the value is OK
        if item.__class__ == CalendarItem:
            # For CalendarItem items where we update 'start' or 'end', we want to send values in the local timezone
            if field.name in ('start', 'end'):
                if type(value) is EWSDate:
                    # EWS always expects a datetime
                    return item.date_to_datetime(field_name=field.name)
                tz_field_name = item.tz_field_for_field_name(field.name).name
                return value.astimezone(getattr(item, tz_field_name))
        return value

    def _get_delete_item_elems(self, field):
        if field.is_required or field.is_required_after_save:
            raise ValueError('%s is a required field and may not be deleted' % field.name)
        for field_path in FieldPath(field=field).expand(version=self.account.version):
            yield self._delete_item_elem(field_path=field_path)

    def _get_set_item_elems(self, item_model, field, value):
        if isinstance(field, IndexedField):
            # Generate either set or delete elements for all combinations of labels and subfields
            supported_labels = field.value_cls.get_field_by_fieldname('label')\
                .supported_choices(version=self.account.version)
            seen_labels = set()
            subfields = field.value_cls.supported_fields(version=self.account.version)
            for v in value:
                seen_labels.add(v.label)
                for subfield in subfields:
                    field_path = FieldPath(field=field, label=v.label, subfield=subfield)
                    subfield_value = getattr(v, subfield.name)
                    if not subfield_value:
                        # Generate delete elements for blank subfield values
                        yield self._delete_item_elem(field_path=field_path)
                    else:
                        # Generate set elements for non-null subfield values
                        yield self._set_item_elem(
                            item_model=item_model,
                            field_path=field_path,
                            value=field.value_cls(**{'label': v.label, subfield.name: subfield_value}),
                        )
                # Generate delete elements for all subfields of all labels not mentioned in the list of values
                for label in (label for label in supported_labels if label not in seen_labels):
                    for subfield in subfields:
                        yield self._delete_item_elem(field_path=FieldPath(field=field, label=label, subfield=subfield))
        else:
            yield self._set_item_elem(item_model=item_model, field_path=FieldPath(field=field), value=value)

    def get_payload(self, items, conflict_resolution, message_disposition, send_meeting_invitations_or_cancellations,
                    suppress_read_receipts):
        # Takes a list of (Item, fieldnames) tuples where 'Item' is a instance of a subclass of Item and 'fieldnames'
        # are the attribute names that were updated. Returns the XML for an UpdateItem call.
        # an UpdateItem request.
        if self.account.version.build >= EXCHANGE_2013_SP1:
            updateitem = create_element(
                'm:%s' % self.SERVICE_NAME,
                attrs=OrderedDict([
                    ('ConflictResolution', conflict_resolution),
                    ('MessageDisposition', message_disposition),
                    ('SendMeetingInvitationsOrCancellations', send_meeting_invitations_or_cancellations),
                    ('SuppressReadReceipts', 'true' if suppress_read_receipts else 'false'),
                ])
            )
        else:
            updateitem = create_element(
                'm:%s' % self.SERVICE_NAME,
                attrs=OrderedDict([
                    ('ConflictResolution', conflict_resolution),
                    ('MessageDisposition', message_disposition),
                    ('SendMeetingInvitationsOrCancellations', send_meeting_invitations_or_cancellations),
                ])
            )
        itemchanges = create_element('m:ItemChanges')
        version = self.account.version
        for item, fieldnames in items:
            if not item.account:
                item.account = self.account
            if not fieldnames:
                raise ValueError('"fieldnames" must not be empty')
            itemchange = create_element('t:ItemChange')
            set_xml_value(itemchange, to_item_id(item, ItemId, version=version), version=version)
            updates = create_element('t:Updates')
            for elem in self._get_item_update_elems(item=item, fieldnames=fieldnames):
                updates.append(elem)
            itemchange.append(updates)
            itemchanges.append(itemchange)
        if not len(itemchanges):
            raise ValueError('"items" must not be empty')
        updateitem.append(itemchanges)
        return updateitem

Ancestors

Class variables

var SERVICE_NAME
var element_container_name

Methods

def call(self, items, conflict_resolution, message_disposition, send_meeting_invitations_or_cancellations, suppress_read_receipts)
Expand source code
def call(self, items, conflict_resolution, message_disposition, send_meeting_invitations_or_cancellations,
         suppress_read_receipts):
    from ..items import CONFLICT_RESOLUTION_CHOICES, MESSAGE_DISPOSITION_CHOICES, \
        SEND_MEETING_INVITATIONS_AND_CANCELLATIONS_CHOICES, SEND_ONLY
    if conflict_resolution not in CONFLICT_RESOLUTION_CHOICES:
        raise ValueError("'conflict_resolution' %s must be one of %s" % (
            conflict_resolution, CONFLICT_RESOLUTION_CHOICES
        ))
    if message_disposition not in MESSAGE_DISPOSITION_CHOICES:
        raise ValueError("'message_disposition' %s must be one of %s" % (
            message_disposition, MESSAGE_DISPOSITION_CHOICES
        ))
    if send_meeting_invitations_or_cancellations not in SEND_MEETING_INVITATIONS_AND_CANCELLATIONS_CHOICES:
        raise ValueError("'send_meeting_invitations_or_cancellations' %s must be one of %s" % (
            send_meeting_invitations_or_cancellations, SEND_MEETING_INVITATIONS_AND_CANCELLATIONS_CHOICES
        ))
    if suppress_read_receipts not in (True, False):
        raise ValueError("'suppress_read_receipts' %s must be True or False" % suppress_read_receipts)
    if message_disposition == SEND_ONLY:
        raise ValueError('Cannot send-only existing objects. Use SendItem service instead')
    return self._elems_to_objs(self._chunked_get_elements(
        self.get_payload,
        items=items,
        conflict_resolution=conflict_resolution,
        message_disposition=message_disposition,
        send_meeting_invitations_or_cancellations=send_meeting_invitations_or_cancellations,
        suppress_read_receipts=suppress_read_receipts,
    ))
def get_payload(self, items, conflict_resolution, message_disposition, send_meeting_invitations_or_cancellations, suppress_read_receipts)
Expand source code
def get_payload(self, items, conflict_resolution, message_disposition, send_meeting_invitations_or_cancellations,
                suppress_read_receipts):
    # Takes a list of (Item, fieldnames) tuples where 'Item' is a instance of a subclass of Item and 'fieldnames'
    # are the attribute names that were updated. Returns the XML for an UpdateItem call.
    # an UpdateItem request.
    if self.account.version.build >= EXCHANGE_2013_SP1:
        updateitem = create_element(
            'm:%s' % self.SERVICE_NAME,
            attrs=OrderedDict([
                ('ConflictResolution', conflict_resolution),
                ('MessageDisposition', message_disposition),
                ('SendMeetingInvitationsOrCancellations', send_meeting_invitations_or_cancellations),
                ('SuppressReadReceipts', 'true' if suppress_read_receipts else 'false'),
            ])
        )
    else:
        updateitem = create_element(
            'm:%s' % self.SERVICE_NAME,
            attrs=OrderedDict([
                ('ConflictResolution', conflict_resolution),
                ('MessageDisposition', message_disposition),
                ('SendMeetingInvitationsOrCancellations', send_meeting_invitations_or_cancellations),
            ])
        )
    itemchanges = create_element('m:ItemChanges')
    version = self.account.version
    for item, fieldnames in items:
        if not item.account:
            item.account = self.account
        if not fieldnames:
            raise ValueError('"fieldnames" must not be empty')
        itemchange = create_element('t:ItemChange')
        set_xml_value(itemchange, to_item_id(item, ItemId, version=version), version=version)
        updates = create_element('t:Updates')
        for elem in self._get_item_update_elems(item=item, fieldnames=fieldnames):
            updates.append(elem)
        itemchange.append(updates)
        itemchanges.append(itemchange)
    if not len(itemchanges):
        raise ValueError('"items" must not be empty')
    updateitem.append(itemchanges)
    return updateitem

Inherited members

class UpdateUserConfiguration (*args, **kwargs)
Expand source code
class UpdateUserConfiguration(EWSAccountService):
    """MSDN:
    https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/updateuserconfiguration-operation
    """

    SERVICE_NAME = 'UpdateUserConfiguration'
    returns_elements = False

    def call(self, user_configuration):
        return self._get_elements(payload=self.get_payload(user_configuration=user_configuration))

    def get_payload(self, user_configuration):
        updateuserconfiguration = create_element('m:%s' % self.SERVICE_NAME)
        set_xml_value(updateuserconfiguration, user_configuration, version=self.account.version)
        return updateuserconfiguration

Ancestors

Class variables

var SERVICE_NAME
var returns_elements

Methods

def call(self, user_configuration)
Expand source code
def call(self, user_configuration):
    return self._get_elements(payload=self.get_payload(user_configuration=user_configuration))
def get_payload(self, user_configuration)
Expand source code
def get_payload(self, user_configuration):
    updateuserconfiguration = create_element('m:%s' % self.SERVICE_NAME)
    set_xml_value(updateuserconfiguration, user_configuration, version=self.account.version)
    return updateuserconfiguration

Inherited members

class UploadItems (*args, **kwargs)
Expand source code
class UploadItems(EWSAccountService):
    """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/uploaditems-operation
    """

    SERVICE_NAME = 'UploadItems'
    element_container_name = '{%s}ItemId' % MNS

    def call(self, items):
        # _pool_requests expects 'items', not 'data'
        return self._elems_to_objs(self._chunked_get_elements(self.get_payload, items=items))

    def get_payload(self, items):
        """Upload given items to given account.

        'items' is an iterable of tuples where the first element is a Folder instance representing the ParentFolder
        that the item will be placed in and the second element is a tuple containing an optional ItemId, an optional
        Item.is_associated boolean, and a Data string returned from an ExportItems.
        call.

        :param items:
        """
        uploaditems = create_element('m:%s' % self.SERVICE_NAME)
        itemselement = create_element('m:Items')
        uploaditems.append(itemselement)
        for parent_folder, (item_id, is_associated, data_str) in items:
            # TODO: The full spec also allows the "UpdateOrCreate" create action.
            item = create_element('t:Item', attrs=dict(CreateAction='Update' if item_id else 'CreateNew'))
            if is_associated is not None:
                item.set('IsAssociated', 'true' if is_associated else 'false')
            parentfolderid = ParentFolderId(parent_folder.id, parent_folder.changekey)
            set_xml_value(item, parentfolderid, version=self.account.version)
            if item_id:
                itemid = to_item_id(item_id, ItemId, version=self.account.version)
                set_xml_value(item, itemid, version=self.account.version)
            add_xml_child(item, 't:Data', data_str)
            itemselement.append(item)
        return uploaditems

    def _elems_to_objs(self, elems):
        for elem in elems:
            if isinstance(elem, Exception):
                yield elem
                continue
            yield elem.get(ItemId.ID_ATTR), elem.get(ItemId.CHANGEKEY_ATTR)

    @classmethod
    def _get_elements_in_container(cls, container):
        return [container]

Ancestors

Class variables

var SERVICE_NAME
var element_container_name

Methods

def call(self, items)
Expand source code
def call(self, items):
    # _pool_requests expects 'items', not 'data'
    return self._elems_to_objs(self._chunked_get_elements(self.get_payload, items=items))
def get_payload(self, items)

Upload given items to given account.

'items' is an iterable of tuples where the first element is a Folder instance representing the ParentFolder that the item will be placed in and the second element is a tuple containing an optional ItemId, an optional Item.is_associated boolean, and a Data string returned from an ExportItems. call.

:param items:

Expand source code
def get_payload(self, items):
    """Upload given items to given account.

    'items' is an iterable of tuples where the first element is a Folder instance representing the ParentFolder
    that the item will be placed in and the second element is a tuple containing an optional ItemId, an optional
    Item.is_associated boolean, and a Data string returned from an ExportItems.
    call.

    :param items:
    """
    uploaditems = create_element('m:%s' % self.SERVICE_NAME)
    itemselement = create_element('m:Items')
    uploaditems.append(itemselement)
    for parent_folder, (item_id, is_associated, data_str) in items:
        # TODO: The full spec also allows the "UpdateOrCreate" create action.
        item = create_element('t:Item', attrs=dict(CreateAction='Update' if item_id else 'CreateNew'))
        if is_associated is not None:
            item.set('IsAssociated', 'true' if is_associated else 'false')
        parentfolderid = ParentFolderId(parent_folder.id, parent_folder.changekey)
        set_xml_value(item, parentfolderid, version=self.account.version)
        if item_id:
            itemid = to_item_id(item_id, ItemId, version=self.account.version)
            set_xml_value(item, itemid, version=self.account.version)
        add_xml_child(item, 't:Data', data_str)
        itemselement.append(item)
    return uploaditems

Inherited members