Module exchangelib.properties
Expand source code
import abc
import binascii
import codecs
import datetime
import logging
import struct
from inspect import getmro
from threading import Lock
from .errors import InvalidTypeError
from .fields import (
WEEKDAY_NAMES,
AssociatedCalendarItemIdField,
Base64Field,
BooleanField,
CharField,
CharListField,
Choice,
ChoiceField,
DateTimeBackedDateField,
DateTimeField,
DictionaryField,
EmailAddressField,
EmailField,
EnumField,
EnumListField,
EWSElementField,
EWSElementListField,
ExtendedPropertyField,
Field,
FieldPath,
FreeBusyStatusField,
GenericEventListField,
IdElementField,
IdField,
IntegerField,
InvalidField,
InvalidFieldForVersion,
MailboxField,
MessageField,
RecipientAddressField,
ReferenceItemIdField,
RoutingTypeField,
SubField,
TextField,
TimeDeltaField,
TimeField,
TransitionListField,
TypeValueField,
UnknownEntriesField,
)
from .util import MNS, TNS, create_element, get_xml_attr, set_xml_value, value_to_xml_text
from .version import EXCHANGE_2013, Build
log = logging.getLogger(__name__)
class Fields(list):
"""A collection type for the FIELDS class attribute. Works like a list but supports fast lookup by name."""
def __init__(self, *fields):
super().__init__(fields)
self._dict = {}
for f in fields:
# Check for duplicate field names
if f.name in self._dict:
raise ValueError(f"Field {f!r} is a duplicate")
self._dict[f.name] = f
def __getitem__(self, idx_or_slice):
# Support fast lookup by name. Make sure slicing returns an instance of this class
if isinstance(idx_or_slice, str):
return self._dict[idx_or_slice]
if isinstance(idx_or_slice, int):
return super().__getitem__(idx_or_slice)
res = super().__getitem__(idx_or_slice)
return self.__class__(*res)
def __add__(self, other):
# Make sure addition returns an instance of this class
res = super().__add__(other)
return self.__class__(*res)
def __iadd__(self, other):
for f in other:
self.append(f)
return self
def __contains__(self, item):
return item in self._dict
def copy(self):
return self.__class__(*self)
def index_by_name(self, field_name):
for i, f in enumerate(self):
if f.name == field_name:
return i
raise ValueError(f"Unknown field name {field_name!r}")
def insert(self, index, field):
if field.name in self._dict:
raise ValueError(f"Field {field!r} is a duplicate")
super().insert(index, field)
self._dict[field.name] = field
def remove(self, field):
super().remove(field)
del self._dict[field.name]
def append(self, field):
super().append(field)
self._dict[field.name] = field
class Body(str):
"""Helper to mark the 'body' field as a complex attribute.
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/body
"""
body_type = "Text"
def __add__(self, other):
# Make sure Body('') + 'foo' returns a Body type
return self.__class__(super().__add__(other))
def __mod__(self, other):
# Make sure Body('%s') % 'foo' returns a Body type
return self.__class__(super().__mod__(other))
def format(self, *args, **kwargs):
# Make sure Body('{}').format('foo') returns a Body type
return self.__class__(super().format(*args, **kwargs))
class HTMLBody(Body):
"""Helper to mark the 'body' field as a complex attribute.
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/body
"""
body_type = "HTML"
class UID(bytes):
"""Helper class to encode Calendar UIDs. See issue #453. Example:
class GlobalObjectId(ExtendedProperty):
distinguished_property_set_id = 'Meeting'
property_id = 3
property_type = 'Binary'
CalendarItem.register('global_object_id', GlobalObjectId)
account.calendar.filter(global_object_id=UID('261cbc18-1f65-5a0a-bd11-23b1e224cc2f'))
"""
_HEADER = binascii.hexlify(
bytearray((0x04, 0x00, 0x00, 0x00, 0x82, 0x00, 0xE0, 0x00, 0x74, 0xC5, 0xB7, 0x10, 0x1A, 0x82, 0xE0, 0x08))
)
_EXCEPTION_REPLACEMENT_TIME = binascii.hexlify(bytearray((0, 0, 0, 0)))
_CREATION_TIME = binascii.hexlify(bytearray((0, 0, 0, 0, 0, 0, 0, 0)))
_RESERVED = binascii.hexlify(bytearray((0, 0, 0, 0, 0, 0, 0, 0)))
# https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxocal/1d3aac05-a7b9-45cc-a213-47f0a0a2c5c1
# https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-asemail/e7424ddc-dd10-431e-a0b7-5c794863370e
# https://stackoverflow.com/questions/42259122
# https://stackoverflow.com/questions/33757805
def __new__(cls, uid):
payload = binascii.hexlify(bytearray(f"vCal-Uid\x01\x00\x00\x00{uid}\x00".encode("ascii")))
length = binascii.hexlify(bytearray(struct.pack("<I", int(len(payload) / 2))))
encoding = b"".join(
[cls._HEADER, cls._EXCEPTION_REPLACEMENT_TIME, cls._CREATION_TIME, cls._RESERVED, length, payload]
)
return super().__new__(cls, codecs.decode(encoding, "hex"))
@classmethod
def to_global_object_id(cls, uid):
"""Converts a UID as returned by EWS to GlobalObjectId format"""
return binascii.unhexlify(uid)
def _mangle(field_name):
return f"__{field_name}"
class EWSMeta(type, metaclass=abc.ABCMeta):
def __new__(mcs, name, bases, kwargs):
# Collect fields defined directly on the class
local_fields = Fields()
for k in tuple(kwargs.keys()):
v = kwargs[k]
if isinstance(v, Field):
v.name = k
local_fields.append(v)
del kwargs[k]
# Build a list of fields defined on this and all base classes
base_fields = Fields()
for base in bases:
if hasattr(base, "FIELDS"):
base_fields += base.FIELDS
# FIELDS defined on a model overrides the base class fields
fields = kwargs.get("FIELDS", base_fields) + local_fields
# Include all fields as class attributes so we can use them as instance attributes
kwargs.update({_mangle(f.name): f for f in fields})
# Calculate __slots__ so we don't have to hard-code it on the model
kwargs["__slots__"] = tuple(f.name for f in fields if f.name not in base_fields) + kwargs.get("__slots__", ())
# FIELDS is mentioned in docs and expected by internal code. Add it here, but only if the class has its own
# fields. Otherwise, we want the implicit FIELDS from the base class (used for injecting custom fields on the
# Folder class, making the custom field available for subclasses).
if local_fields:
kwargs["FIELDS"] = fields
klass = super().__new__(mcs, name, bases, kwargs)
klass._slots_keys = mcs._get_slots_keys(klass)
return klass
@staticmethod
def _get_slots_keys(klass):
seen = set()
keys = []
for c in reversed(getmro(klass)):
if not hasattr(c, "__slots__"):
continue
for k in c.__slots__:
if k in seen:
# We allow duplicate keys because we don't want to require subclasses of e.g.
# ExtendedProperty to define an empty __slots__ class attribute.
continue
keys.append(k)
seen.add(k)
return keys
def __getattribute__(cls, k):
"""Return Field instances via their mangled class attribute"""
try:
return super().__getattribute__("__dict__")[_mangle(k)]
except KeyError:
return super().__getattribute__(k)
class EWSElement(metaclass=EWSMeta):
"""Base class for all XML element implementations."""
ELEMENT_NAME = None # The name of the XML tag
FIELDS = Fields() # A list of attributes supported by this item class, ordered the same way as in EWS documentation
NAMESPACE = TNS # The XML tag namespace. Either TNS or MNS
_fields_lock = Lock()
def __init__(self, **kwargs):
for f in self.FIELDS:
setattr(self, f.name, kwargs.pop(f.name, None))
if kwargs:
raise AttributeError(f"{sorted(kwargs.keys())!r} are invalid kwargs for this class")
def __setattr__(self, key, value):
# Avoid silently accepting spelling errors to field names that are not set via __init__. We need to be able to
# set values for predefined and registered fields, whatever non-field attributes this class defines, and
# property setters.
if key in self.FIELDS:
return super().__setattr__(key, value)
if key in self._slots_keys:
return super().__setattr__(key, value)
if hasattr(self, key):
# Property setters
return super().__setattr__(key, value)
raise AttributeError(
f"{key!r} is not a valid attribute. See {self.__class__.__name__}.FIELDS for valid field names"
)
def clean(self, version=None):
# Validate attribute values using the field validator
for f in self.FIELDS:
if version and not f.supports_version(version):
continue
if isinstance(f, ExtendedPropertyField) and not hasattr(self, f.name):
# The extended field may have been registered after this item was created. Set default values.
setattr(self, f.name, f.clean(None, version=version))
continue
val = getattr(self, f.name)
setattr(self, f.name, f.clean(val, version=version))
@staticmethod
def _clear(elem):
# Clears an XML element to reduce memory consumption
elem.clear()
# Don't attempt to clean up previous siblings. We may not have parsed them yet.
parent = elem.getparent()
if parent is None:
return
parent.remove(elem)
@classmethod
def from_xml(cls, elem, account):
kwargs = {f.name: f.from_xml(elem=elem, account=account) for f in cls.FIELDS}
cls._clear(elem)
return cls(**kwargs)
def to_xml(self, version):
self.clean(version=version)
# WARNING: The order of addition of XML elements is VERY important. Exchange expects XML elements in a
# specific, non-documented order and will fail with meaningless errors if the order is wrong.
# Collect attributes
attrs = {}
for f in self.attribute_fields():
if f.is_read_only:
continue
value = getattr(self, f.name)
if value is None or (f.is_list and not value):
continue
attrs[f.field_uri] = value_to_xml_text(getattr(self, f.name))
# Create element with attributes
elem = create_element(self.request_tag(), attrs=attrs)
# Add elements and values
for f in self.supported_fields(version=version):
if f.is_read_only:
continue
value = getattr(self, f.name)
if value is None or (f.is_list and not value):
continue
set_xml_value(elem, f.to_xml(value, version=version))
return elem
@classmethod
def request_tag(cls):
if not cls.ELEMENT_NAME:
raise ValueError(f"Class {cls} is missing the ELEMENT_NAME attribute")
return {
TNS: f"t:{cls.ELEMENT_NAME}",
MNS: f"m:{cls.ELEMENT_NAME}",
}[cls.NAMESPACE]
@classmethod
def response_tag(cls):
if not cls.NAMESPACE:
raise ValueError(f"Class {cls} is missing the NAMESPACE attribute")
if not cls.ELEMENT_NAME:
raise ValueError(f"Class {cls} is missing the ELEMENT_NAME attribute")
return f"{{{cls.NAMESPACE}}}{cls.ELEMENT_NAME}"
@classmethod
def attribute_fields(cls):
return tuple(f for f in cls.FIELDS if f.is_attribute)
@classmethod
def supported_fields(cls, version):
"""Return the fields supported by the given server version."""
return tuple(f for f in cls.FIELDS if not f.is_attribute and f.supports_version(version))
@classmethod
def get_field_by_fieldname(cls, fieldname):
try:
return cls.FIELDS[fieldname]
except KeyError:
raise InvalidField(f"{fieldname!r} is not a valid field name on {cls.__name__}")
@classmethod
def validate_field(cls, field, version):
"""Take a list of fieldnames, Field or FieldPath objects pointing to item fields, and check that they are
valid for the given version.
:param field:
:param version:
"""
# Allow both Field and FieldPath instances and string field paths as input
if isinstance(field, str):
field = cls.get_field_by_fieldname(fieldname=field)
elif isinstance(field, FieldPath):
field = field.field
cls.get_field_by_fieldname(fieldname=field.name) # Will raise if field name is invalid
if not field.supports_version(version):
# The field exists but is not valid for this version
raise InvalidFieldForVersion(
f"Field {field.name!r} is not supported on server version {version} "
f"(supported from: {field.supported_from}, deprecated from: {field.deprecated_from})"
)
@classmethod
def add_field(cls, field, insert_after):
"""Insert a new field at the preferred place in the tuple and update the slots cache.
:param field:
:param insert_after:
"""
with cls._fields_lock:
idx = cls.FIELDS.index_by_name(insert_after) + 1
# This class may not have its own FIELDS attribute. Make sure not to edit an attribute belonging to a parent
# class.
cls.FIELDS.insert(idx, field)
setattr(cls, _mangle(field.name), field)
@classmethod
def remove_field(cls, field):
"""Remove the given field and and update the slots cache.
:param field:
"""
with cls._fields_lock:
# This class may not have its own FIELDS attribute. Make sure not to edit an attribute belonging to a parent
# class.
cls.FIELDS.remove(field)
delattr(cls, _mangle(field.name))
def __eq__(self, other):
return hash(self) == hash(other)
def __hash__(self):
return hash(
tuple(tuple(getattr(self, f.name) or ()) if f.is_list else getattr(self, f.name) for f in self.FIELDS)
)
def _field_vals(self):
field_vals = [] # Keep sorting
for f in self.FIELDS:
val = getattr(self, f.name)
if isinstance(f, EnumField) and isinstance(val, int):
val = f.as_string(val)
field_vals.append((f.name, val))
return field_vals
def __str__(self):
args_str = ", ".join(f"{name}={val!r}" for name, val in self._field_vals() if val is not None)
return f"{self.__class__.__name__}({args_str})"
def __repr__(self):
args_str = ", ".join(f"{name}={val!r}" for name, val in self._field_vals())
return f"{self.__class__.__name__}({args_str})"
class MessageHeader(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/internetmessageheader"""
ELEMENT_NAME = "InternetMessageHeader"
name = TextField(field_uri="HeaderName", is_attribute=True)
value = SubField()
class BaseItemId(EWSElement, metaclass=EWSMeta):
"""Base class for ItemId elements."""
ID_ATTR = None
CHANGEKEY_ATTR = None
def __init__(self, *args, **kwargs):
if not kwargs:
# Allow to set attributes without keyword
kwargs = dict(zip(self._slots_keys, args))
super().__init__(**kwargs)
class ItemId(BaseItemId):
"""'id' and 'changekey' are UUIDs generated by Exchange.
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/itemid
"""
ELEMENT_NAME = "ItemId"
ID_ATTR = "Id"
CHANGEKEY_ATTR = "ChangeKey"
id = IdField(field_uri=ID_ATTR, is_required=True)
changekey = IdField(field_uri=CHANGEKEY_ATTR, is_required=False)
class ParentItemId(ItemId):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/parentitemid"""
ELEMENT_NAME = "ParentItemId"
NAMESPACE = MNS
class RootItemId(BaseItemId):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/rootitemid"""
ELEMENT_NAME = "RootItemId"
NAMESPACE = MNS
ID_ATTR = "RootItemId"
CHANGEKEY_ATTR = "RootItemChangeKey"
id = IdField(field_uri=ID_ATTR, is_required=True)
changekey = IdField(field_uri=CHANGEKEY_ATTR, is_required=True)
class AssociatedCalendarItemId(ItemId):
"""MSDN:
https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/associatedcalendaritemid
"""
ELEMENT_NAME = "AssociatedCalendarItemId"
class ConversationId(ItemId):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/conversationid"""
ELEMENT_NAME = "ConversationId"
# ChangeKey attribute is sometimes required, see MSDN link
class ParentFolderId(ItemId):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/parentfolderid"""
ELEMENT_NAME = "ParentFolderId"
class ReferenceItemId(ItemId):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/referenceitemid"""
ELEMENT_NAME = "ReferenceItemId"
class PersonaId(ItemId):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/personaid"""
ELEMENT_NAME = "PersonaId"
NAMESPACE = MNS
@classmethod
def response_tag(cls):
# This element is in MNS in the request and TNS in the response...
return f"{{{TNS}}}{cls.ELEMENT_NAME}"
class SourceId(ItemId):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/sourceid"""
ELEMENT_NAME = "SourceId"
class FolderId(ItemId):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/folderid"""
ELEMENT_NAME = "FolderId"
class RecurringMasterItemId(BaseItemId):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/recurringmasteritemid"""
ELEMENT_NAME = "RecurringMasterItemId"
ID_ATTR = "OccurrenceId"
CHANGEKEY_ATTR = "ChangeKey"
id = IdField(field_uri=ID_ATTR, is_required=True)
changekey = IdField(field_uri=CHANGEKEY_ATTR, is_required=False)
class OccurrenceItemId(BaseItemId):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/occurrenceitemid"""
ELEMENT_NAME = "OccurrenceItemId"
ID_ATTR = "RecurringMasterId"
CHANGEKEY_ATTR = "ChangeKey"
id = IdField(field_uri=ID_ATTR, is_required=True)
changekey = IdField(field_uri=CHANGEKEY_ATTR, is_required=False)
instance_index = IntegerField(field_uri="InstanceIndex", is_attribute=True, is_required=True, min=1)
class MovedItemId(ItemId):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/moveditemid"""
ELEMENT_NAME = "MovedItemId"
NAMESPACE = MNS
@classmethod
def id_from_xml(cls, elem):
item = cls.from_xml(elem=elem, account=None)
return item.id, item.changekey
class OldItemId(ItemId):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/oldfolderid"""
ELEMENT_NAME = "OldItemId"
class OldFolderId(FolderId):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/olditemid"""
ELEMENT_NAME = "OldFolderId"
class OldParentFolderId(ParentFolderId):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/oldparentfolderid"""
ELEMENT_NAME = "OldParentFolderId"
class Mailbox(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/mailbox"""
ELEMENT_NAME = "Mailbox"
MAILBOX = "Mailbox"
ONE_OFF = "OneOff"
MAILBOX_TYPE_CHOICES = {
Choice(MAILBOX),
Choice("PublicDL"),
Choice("PrivateDL"),
Choice("Contact"),
Choice("PublicFolder"),
Choice("Unknown"),
Choice(ONE_OFF),
Choice("GroupMailbox", supported_from=EXCHANGE_2013),
}
name = TextField(field_uri="Name")
email_address = EmailAddressField(field_uri="EmailAddress")
# RoutingType values are not restricted:
# https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/routingtype-emailaddresstype
routing_type = TextField(field_uri="RoutingType", default="SMTP")
mailbox_type = ChoiceField(field_uri="MailboxType", choices=MAILBOX_TYPE_CHOICES, default=MAILBOX)
item_id = EWSElementField(value_cls=ItemId, is_read_only=True)
def clean(self, version=None):
super().clean(version=version)
if self.mailbox_type != self.ONE_OFF and not self.email_address and not self.item_id:
# A OneOff Mailbox (a one-off member of a personal distribution list) may lack these fields, but other
# Mailboxes require at least one. See also "Remarks" section of
# https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/mailbox
raise ValueError(f"Mailbox type {self.mailbox_type!r} must have either 'email_address' or 'item_id' set")
def __hash__(self):
# Exchange may add 'mailbox_type' and 'name' on insert. We're satisfied if the item_id or email address matches.
if self.item_id:
return hash(self.item_id)
if self.email_address:
return hash(self.email_address.lower())
return super().__hash__()
class DLMailbox(Mailbox):
"""Like Mailbox, but creates elements in the 'messages' namespace when sending requests."""
NAMESPACE = MNS
class SendingAs(Mailbox):
"""Like Mailbox, but creates elements in the 'messages' namespace when sending requests.
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/sendingas
"""
ELEMENT_NAME = "SendingAs"
NAMESPACE = MNS
class RecipientAddress(Mailbox):
"""Like Mailbox, but with a different tag name.
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/recipientaddress
"""
ELEMENT_NAME = "RecipientAddress"
class EmailAddress(Mailbox):
"""Like Mailbox, but with a different tag name.
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/emailaddress-emailaddresstype
"""
ELEMENT_NAME = "EmailAddress"
class Address(Mailbox):
"""Like Mailbox, but with a different tag name.
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/address-emailaddresstype
"""
ELEMENT_NAME = "Address"
class AvailabilityMailbox(EWSElement):
"""Like Mailbox, but with slightly different attributes.
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/mailbox-availability
"""
ELEMENT_NAME = "Mailbox"
name = TextField(field_uri="Name")
email_address = EmailAddressField(field_uri="Address", is_required=True)
# RoutingType values restricted to EX and SMTP:
# https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/routingtype-emailaddress
routing_type = RoutingTypeField(field_uri="RoutingType")
def __hash__(self):
# Exchange may add 'name' on insert. We're satisfied if the email address matches.
if self.email_address:
return hash(self.email_address.lower())
return super().__hash__()
@classmethod
def from_mailbox(cls, mailbox):
if not isinstance(mailbox, Mailbox):
raise InvalidTypeError("mailbox", mailbox, Mailbox)
return cls(name=mailbox.name, email_address=mailbox.email_address, routing_type=mailbox.routing_type)
class Email(AvailabilityMailbox):
"""Like AvailabilityMailbox, but with a different tag name.
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/email-emailaddresstype
"""
ELEMENT_NAME = "Email"
class MailboxData(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/mailboxdata"""
ELEMENT_NAME = "MailboxData"
ATTENDEE_TYPES = {"Optional", "Organizer", "Required", "Resource", "Room"}
email = EmailField()
attendee_type = ChoiceField(field_uri="AttendeeType", choices={Choice(c) for c in ATTENDEE_TYPES})
exclude_conflicts = BooleanField(field_uri="ExcludeConflicts")
class DistinguishedFolderId(FolderId):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/distinguishedfolderid"""
ELEMENT_NAME = "DistinguishedFolderId"
mailbox = MailboxField()
def clean(self, version=None):
from .folders import PublicFoldersRoot
super().clean(version=version)
if self.id == PublicFoldersRoot.DISTINGUISHED_FOLDER_ID:
# Avoid "ErrorInvalidOperation: It is not valid to specify a mailbox with the public folder root" from EWS
self.mailbox = None
class TimeWindow(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/timewindow"""
ELEMENT_NAME = "TimeWindow"
start = DateTimeField(field_uri="StartTime", is_required=True)
end = DateTimeField(field_uri="EndTime", is_required=True)
def clean(self, version=None):
if self.start >= self.end:
raise ValueError(f"'start' must be less than 'end' ({self.start} -> {self.end})")
super().clean(version=version)
class FreeBusyViewOptions(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/freebusyviewoptions"""
ELEMENT_NAME = "FreeBusyViewOptions"
REQUESTED_VIEWS = {"MergedOnly", "FreeBusy", "FreeBusyMerged", "Detailed", "DetailedMerged"}
time_window = EWSElementField(value_cls=TimeWindow, is_required=True)
# Interval value is in minutes
merged_free_busy_interval = IntegerField(
field_uri="MergedFreeBusyIntervalInMinutes", min=5, max=1440, default=30, is_required=True
)
requested_view = ChoiceField(
field_uri="RequestedView", choices={Choice(c) for c in REQUESTED_VIEWS}, is_required=True
) # Choice('None') is also valid, but only for responses
class Attendee(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/attendee"""
ELEMENT_NAME = "Attendee"
RESPONSE_TYPES = {"Unknown", "Organizer", "Tentative", "Accept", "Decline", "NoResponseReceived"}
mailbox = MailboxField(is_required=True)
response_type = ChoiceField(
field_uri="ResponseType", choices={Choice(c) for c in RESPONSE_TYPES}, default="Unknown"
)
last_response_time = DateTimeField(field_uri="LastResponseTime")
def __hash__(self):
return hash(self.mailbox)
class TimeZoneTransition(EWSElement, metaclass=EWSMeta):
"""Base class for StandardTime and DaylightTime classes."""
bias = IntegerField(field_uri="Bias", is_required=True) # Offset from the default bias, in minutes
time = TimeField(field_uri="Time", is_required=True)
occurrence = IntegerField(field_uri="DayOrder", is_required=True) # n'th occurrence of weekday in iso_month
iso_month = IntegerField(field_uri="Month", is_required=True)
weekday = EnumField(field_uri="DayOfWeek", enum=WEEKDAY_NAMES, is_required=True)
# 'Year' is not implemented yet
@classmethod
def from_xml(cls, elem, account):
res = super().from_xml(elem, account)
# Some parts of EWS use '5' to mean 'last occurrence in month', others use '-1'. Let's settle on '5' because
# only '5' is accepted in requests.
if res.occurrence == -1:
res.occurrence = 5
return res
def clean(self, version=None):
super().clean(version=version)
if self.occurrence == -1:
# See from_xml()
self.occurrence = 5
class StandardTime(TimeZoneTransition):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/standardtime"""
ELEMENT_NAME = "StandardTime"
class DaylightTime(TimeZoneTransition):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/daylighttime"""
ELEMENT_NAME = "DaylightTime"
class TimeZone(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/timezone-availability"""
ELEMENT_NAME = "TimeZone"
bias = IntegerField(field_uri="Bias", is_required=True) # Standard (non-DST) offset from UTC, in minutes
standard_time = EWSElementField(value_cls=StandardTime)
daylight_time = EWSElementField(value_cls=DaylightTime)
def to_server_timezone(self, timezones, for_year):
"""Return the Microsoft timezone ID corresponding to this timezone. There may not be a match at all, and there
may be multiple matches. If so, return a random timezone ID.
:param timezones: A list of server timezones, as returned by
Protocol.get_timezones(return_full_timezone_data=True)
:param for_year: return: A Microsoft timezone ID, as a string
:return: A Microsoft timezone ID, as a string
"""
candidates = set()
for tz_definition in timezones:
candidate = self.from_server_timezone(
tz_definition=tz_definition,
for_year=for_year,
)
if candidate == self:
log.debug("Found exact candidate: %s (%s)", tz_definition.id, tz_definition.name)
# We prefer this timezone over anything else. Return immediately.
return tz_definition.id
# Reduce list based on base bias and standard / daylight bias values
if candidate.bias != self.bias:
continue
if candidate.standard_time is None:
if self.standard_time is not None:
continue
else:
if self.standard_time is None:
continue
if candidate.standard_time.bias != self.standard_time.bias:
continue
if candidate.daylight_time is None:
if self.daylight_time is not None:
continue
else:
if self.daylight_time is None:
continue
if candidate.daylight_time.bias != self.daylight_time.bias:
continue
log.debug("Found candidate with matching biases: %s (%s)", tz_definition.id, tz_definition.name)
candidates.add(tz_definition.id)
if not candidates:
raise ValueError("No server timezones match this timezone definition")
if len(candidates) == 1:
log.info("Could not find an exact timezone match for %s. Selecting the best candidate", self)
else:
log.warning("Could not find an exact timezone match for %s. Selecting a random candidate", self)
return candidates.pop()
@classmethod
def from_server_timezone(cls, tz_definition, for_year):
# Creates a TimeZone object from the result of a GetServerTimeZones call with full timezone data
std_time, daylight_time, period = tz_definition.get_std_and_dst(for_year=for_year)
return cls(bias=period.bias_in_minutes, standard_time=std_time, daylight_time=daylight_time)
class CalendarView(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/calendarview"""
ELEMENT_NAME = "CalendarView"
NAMESPACE = MNS
start = DateTimeField(field_uri="StartDate", is_required=True, is_attribute=True)
end = DateTimeField(field_uri="EndDate", is_required=True, is_attribute=True)
max_items = IntegerField(field_uri="MaxEntriesReturned", min=1, is_attribute=True)
def clean(self, version=None):
super().clean(version=version)
if self.end < self.start:
raise ValueError("'start' must be before 'end'")
class CalendarEventDetails(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/calendareventdetails"""
ELEMENT_NAME = "CalendarEventDetails"
id = CharField(field_uri="ID")
subject = CharField(field_uri="Subject")
location = CharField(field_uri="Location")
is_meeting = BooleanField(field_uri="IsMeeting")
is_recurring = BooleanField(field_uri="IsRecurring")
is_exception = BooleanField(field_uri="IsException")
is_reminder_set = BooleanField(field_uri="IsReminderSet")
is_private = BooleanField(field_uri="IsPrivate")
class CalendarEvent(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/calendarevent"""
ELEMENT_NAME = "CalendarEvent"
start = DateTimeField(field_uri="StartTime")
end = DateTimeField(field_uri="EndTime")
busy_type = FreeBusyStatusField(field_uri="BusyType", is_required=True, default="Busy")
details = EWSElementField(value_cls=CalendarEventDetails)
class WorkingPeriod(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/workingperiod"""
ELEMENT_NAME = "WorkingPeriod"
weekdays = EnumListField(field_uri="DayOfWeek", enum=WEEKDAY_NAMES, is_required=True)
start = TimeField(field_uri="StartTimeInMinutes", is_required=True)
end = TimeField(field_uri="EndTimeInMinutes", is_required=True)
class FreeBusyView(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/freebusyview"""
ELEMENT_NAME = "FreeBusyView"
NAMESPACE = MNS
view_type = ChoiceField(
field_uri="FreeBusyViewType",
choices={
Choice("None"),
Choice("MergedOnly"),
Choice("FreeBusy"),
Choice("FreeBusyMerged"),
Choice("Detailed"),
Choice("DetailedMerged"),
},
is_required=True,
)
# A string of digits. Each digit points to a position in .fields.FREE_BUSY_CHOICES
merged = CharField(field_uri="MergedFreeBusy")
calendar_events = EWSElementListField(field_uri="CalendarEventArray", value_cls=CalendarEvent)
# WorkingPeriod is located inside the WorkingPeriodArray element which is inside the WorkingHours element
working_hours = EWSElementListField(field_uri="WorkingPeriodArray", value_cls=WorkingPeriod)
# TimeZone is also inside the WorkingHours element. It contains information about the timezone which the
# account is located in.
working_hours_timezone = EWSElementField(value_cls=TimeZone)
@classmethod
def from_xml(cls, elem, account):
kwargs = {}
working_hours_elem = elem.find(f"{{{TNS}}}WorkingHours")
for f in cls.FIELDS:
if f.name in ("working_hours", "working_hours_timezone"):
if working_hours_elem is None:
continue
kwargs[f.name] = f.from_xml(elem=working_hours_elem, account=account)
continue
kwargs[f.name] = f.from_xml(elem=elem, account=account)
cls._clear(elem)
return cls(**kwargs)
class RoomList(Mailbox):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/roomlist"""
ELEMENT_NAME = "RoomList"
NAMESPACE = MNS
@classmethod
def response_tag(cls):
# In a GetRoomLists response, room lists are delivered as Address elements. See
# https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/address-emailaddresstype
return f"{{{TNS}}}Address"
class Room(Mailbox):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/room"""
ELEMENT_NAME = "Room"
@classmethod
def from_xml(cls, elem, account):
id_elem = elem.find(f"{{{TNS}}}Id")
item_id_elem = id_elem.find(ItemId.response_tag())
kwargs = dict(
name=get_xml_attr(id_elem, f"{{{TNS}}}Name"),
email_address=get_xml_attr(id_elem, f"{{{TNS}}}EmailAddress"),
mailbox_type=get_xml_attr(id_elem, f"{{{TNS}}}MailboxType"),
item_id=ItemId.from_xml(elem=item_id_elem, account=account) if item_id_elem else None,
)
cls._clear(elem)
return cls(**kwargs)
class Member(EWSElement):
"""MSDN:
https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/member-ex15websvcsotherref
"""
ELEMENT_NAME = "Member"
mailbox = MailboxField(is_required=True)
status = ChoiceField(
field_uri="Status", choices={Choice("Unrecognized"), Choice("Normal"), Choice("Demoted")}, default="Normal"
)
def __hash__(self):
return hash(self.mailbox)
class UserId(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/userid"""
ELEMENT_NAME = "UserId"
sid = CharField(field_uri="SID")
primary_smtp_address = EmailAddressField(field_uri="PrimarySmtpAddress")
display_name = CharField(field_uri="DisplayName")
distinguished_user = ChoiceField(field_uri="DistinguishedUser", choices={Choice("Default"), Choice("Anonymous")})
external_user_identity = CharField(field_uri="ExternalUserIdentity")
class BasePermission(EWSElement, metaclass=EWSMeta):
"""Base class for the Permission and CalendarPermission classes"""
PERMISSION_ENUM = {Choice("None"), Choice("Owned"), Choice("All")}
can_create_items = BooleanField(field_uri="CanCreateItems", default=False)
can_create_subfolders = BooleanField(field_uri="CanCreateSubfolders", default=False)
is_folder_owner = BooleanField(field_uri="IsFolderOwner", default=False)
is_folder_visible = BooleanField(field_uri="IsFolderVisible", default=False)
is_folder_contact = BooleanField(field_uri="IsFolderContact", default=False)
edit_items = ChoiceField(field_uri="EditItems", choices=PERMISSION_ENUM, default="None")
delete_items = ChoiceField(field_uri="DeleteItems", choices=PERMISSION_ENUM, default="None")
read_items = ChoiceField(field_uri="ReadItems", choices={Choice("None"), Choice("FullDetails")}, default="None")
user_id = EWSElementField(value_cls=UserId, is_required=True)
class Permission(BasePermission):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/permission"""
ELEMENT_NAME = "Permission"
LEVEL_CHOICES = (
"None",
"Owner",
"PublishingEditor",
"Editor",
"PublishingAuthor",
"Author",
"NoneditingAuthor",
"Reviewer",
"Contributor",
"Custom",
)
permission_level = ChoiceField(
field_uri="CalendarPermissionLevel", choices={Choice(c) for c in LEVEL_CHOICES}, default=LEVEL_CHOICES[0]
)
class CalendarPermission(BasePermission):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/calendarpermission"""
ELEMENT_NAME = "CalendarPermission"
LEVEL_CHOICES = (
"None",
"Owner",
"PublishingEditor",
"Editor",
"PublishingAuthor",
"Author",
"NoneditingAuthor",
"Reviewer",
"Contributor",
"FreeBusyTimeOnly",
"FreeBusyTimeAndSubjectAndLocation",
"Custom",
)
calendar_permission_level = ChoiceField(
field_uri="CalendarPermissionLevel", choices={Choice(c) for c in LEVEL_CHOICES}, default=LEVEL_CHOICES[0]
)
class PermissionSet(EWSElement):
"""MSDN:
https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/permissionset-permissionsettype
and
https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/permissionset-calendarpermissionsettype
"""
# For simplicity, we implement the two distinct but equally names elements as one class.
ELEMENT_NAME = "PermissionSet"
permissions = EWSElementListField(field_uri="Permissions", value_cls=Permission)
calendar_permissions = EWSElementListField(field_uri="CalendarPermissions", value_cls=CalendarPermission)
unknown_entries = UnknownEntriesField(field_uri="UnknownEntries")
class EffectiveRights(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/effectiverights"""
ELEMENT_NAME = "EffectiveRights"
create_associated = BooleanField(field_uri="CreateAssociated", default=False)
create_contents = BooleanField(field_uri="CreateContents", default=False)
create_hierarchy = BooleanField(field_uri="CreateHierarchy", default=False)
delete = BooleanField(field_uri="Delete", default=False)
modify = BooleanField(field_uri="Modify", default=False)
read = BooleanField(field_uri="Read", default=False)
view_private_items = BooleanField(field_uri="ViewPrivateItems", default=False)
def __contains__(self, item):
return getattr(self, item, False)
class DelegatePermissions(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/delegatepermissions"""
ELEMENT_NAME = "DelegatePermissions"
PERMISSION_LEVEL_CHOICES = {
Choice("None"),
Choice("Editor"),
Choice("Reviewer"),
Choice("Author"),
Choice("Custom"),
}
calendar_folder_permission_level = ChoiceField(
field_uri="CalendarFolderPermissionLevel", choices=PERMISSION_LEVEL_CHOICES, default="None"
)
tasks_folder_permission_level = ChoiceField(
field_uri="TasksFolderPermissionLevel", choices=PERMISSION_LEVEL_CHOICES, default="None"
)
inbox_folder_permission_level = ChoiceField(
field_uri="InboxFolderPermissionLevel", choices=PERMISSION_LEVEL_CHOICES, default="None"
)
contacts_folder_permission_level = ChoiceField(
field_uri="ContactsFolderPermissionLevel", choices=PERMISSION_LEVEL_CHOICES, default="None"
)
notes_folder_permission_level = ChoiceField(
field_uri="NotesFolderPermissionLevel", choices=PERMISSION_LEVEL_CHOICES, default="None"
)
journal_folder_permission_level = ChoiceField(
field_uri="JournalFolderPermissionLevel", choices=PERMISSION_LEVEL_CHOICES, default="None"
)
class DelegateUser(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/delegateuser"""
ELEMENT_NAME = "DelegateUser"
NAMESPACE = MNS
user_id = EWSElementField(value_cls=UserId)
delegate_permissions = EWSElementField(value_cls=DelegatePermissions)
receive_copies_of_meeting_messages = BooleanField(field_uri="ReceiveCopiesOfMeetingMessages", default=False)
view_private_items = BooleanField(field_uri="ViewPrivateItems", default=False)
class SearchableMailbox(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/searchablemailbox"""
ELEMENT_NAME = "SearchableMailbox"
guid = CharField(field_uri="Guid")
primary_smtp_address = EmailAddressField(field_uri="PrimarySmtpAddress")
is_external = BooleanField(field_uri="IsExternalMailbox")
external_email = EmailAddressField(field_uri="ExternalEmailAddress")
display_name = CharField(field_uri="DisplayName")
is_membership_group = BooleanField(field_uri="IsMembershipGroup")
reference_id = CharField(field_uri="ReferenceId")
class FailedMailbox(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/failedmailbox"""
ELEMENT_NAME = "FailedMailbox"
mailbox = CharField(field_uri="Mailbox")
error_code = IntegerField(field_uri="ErrorCode")
error_message = CharField(field_uri="ErrorMessage")
is_archive = BooleanField(field_uri="IsArchive")
# MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/mailtipsrequested
MAIL_TIPS_TYPES = (
"All",
"OutOfOfficeMessage",
"MailboxFullStatus",
"CustomMailTip",
"ExternalMemberCount",
"TotalMemberCount",
"MaxMessageSize",
"DeliveryRestriction",
"ModerationStatus",
"InvalidRecipient",
)
class OutOfOffice(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/outofoffice"""
ELEMENT_NAME = "OutOfOffice"
reply_body = MessageField(field_uri="ReplyBody")
start = DateTimeField(field_uri="StartTime", is_required=False)
end = DateTimeField(field_uri="EndTime", is_required=False)
@classmethod
def duration_to_start_end(cls, elem, account):
kwargs = {}
duration = elem.find(f"{{{TNS}}}Duration")
if duration is not None:
for attr in ("start", "end"):
f = cls.get_field_by_fieldname(attr)
kwargs[attr] = f.from_xml(elem=duration, account=account)
return kwargs
@classmethod
def from_xml(cls, elem, account):
kwargs = {}
for attr in ("reply_body",):
f = cls.get_field_by_fieldname(attr)
kwargs[attr] = f.from_xml(elem=elem, account=account)
kwargs.update(cls.duration_to_start_end(elem=elem, account=account))
cls._clear(elem)
return cls(**kwargs)
class MailTips(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/mailtips"""
ELEMENT_NAME = "MailTips"
NAMESPACE = MNS
recipient_address = RecipientAddressField()
pending_mail_tips = ChoiceField(field_uri="PendingMailTips", choices={Choice(c) for c in MAIL_TIPS_TYPES})
out_of_office = EWSElementField(value_cls=OutOfOffice)
mailbox_full = BooleanField(field_uri="MailboxFull")
custom_mail_tip = TextField(field_uri="CustomMailTip")
total_member_count = IntegerField(field_uri="TotalMemberCount")
external_member_count = IntegerField(field_uri="ExternalMemberCount")
max_message_size = IntegerField(field_uri="MaxMessageSize")
delivery_restricted = BooleanField(field_uri="DeliveryRestricted")
is_moderated = BooleanField(field_uri="IsModerated")
invalid_recipient = BooleanField(field_uri="InvalidRecipient")
ENTRY_ID = "EntryId" # The base64-encoded PR_ENTRYID property
EWS_ID = "EwsId" # The EWS format used in Exchange 2007 SP1 and later
EWS_LEGACY_ID = "EwsLegacyId" # The EWS format used in Exchange 2007 before SP1
HEX_ENTRY_ID = "HexEntryId" # The hexadecimal representation of the PR_ENTRYID property
OWA_ID = "OwaId" # The OWA format for Exchange 2007 and 2010
STORE_ID = "StoreId" # The Exchange Store format
# IdFormat enum
ID_FORMATS = (ENTRY_ID, EWS_ID, EWS_LEGACY_ID, HEX_ENTRY_ID, OWA_ID, STORE_ID)
class AlternateId(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/alternateid"""
ELEMENT_NAME = "AlternateId"
id = CharField(field_uri="Id", is_required=True, is_attribute=True)
format = ChoiceField(
field_uri="Format", is_required=True, is_attribute=True, choices={Choice(c) for c in ID_FORMATS}
)
mailbox = EmailAddressField(field_uri="Mailbox", is_required=True, is_attribute=True)
is_archive = BooleanField(field_uri="IsArchive", is_required=False, is_attribute=True)
@classmethod
def response_tag(cls):
# This element is in TNS in the request and MNS in the response...
return f"{{{MNS}}}{cls.ELEMENT_NAME}"
class AlternatePublicFolderId(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/alternatepublicfolderid"""
ELEMENT_NAME = "AlternatePublicFolderId"
folder_id = CharField(field_uri="FolderId", is_required=True, is_attribute=True)
format = ChoiceField(
field_uri="Format", is_required=True, is_attribute=True, choices={Choice(c) for c in ID_FORMATS}
)
class AlternatePublicFolderItemId(EWSElement):
"""MSDN:
https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/alternatepublicfolderitemid
"""
ELEMENT_NAME = "AlternatePublicFolderItemId"
folder_id = CharField(field_uri="FolderId", is_required=True, is_attribute=True)
format = ChoiceField(
field_uri="Format", is_required=True, is_attribute=True, choices={Choice(c) for c in ID_FORMATS}
)
item_id = CharField(field_uri="ItemId", is_required=True, is_attribute=True)
class FieldURI(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/fielduri"""
ELEMENT_NAME = "FieldURI"
field_uri = CharField(field_uri="FieldURI", is_attribute=True, is_required=True)
class IndexedFieldURI(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/indexedfielduri"""
ELEMENT_NAME = "IndexedFieldURI"
field_uri = CharField(field_uri="FieldURI", is_attribute=True, is_required=True)
field_index = CharField(field_uri="FieldIndex", is_attribute=True, is_required=True)
class ExtendedFieldURI(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/extendedfielduri"""
ELEMENT_NAME = "ExtendedFieldURI"
distinguished_property_set_id = CharField(field_uri="DistinguishedPropertySetId", is_attribute=True)
property_set_id = CharField(field_uri="PropertySetId", is_attribute=True)
property_tag = CharField(field_uri="PropertyTag", is_attribute=True)
property_name = CharField(field_uri="PropertyName", is_attribute=True)
property_id = CharField(field_uri="PropertyId", is_attribute=True)
property_type = CharField(field_uri="PropertyType", is_attribute=True)
class ExceptionFieldURI(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/exceptionfielduri"""
ELEMENT_NAME = "ExceptionFieldURI"
field_uri = CharField(field_uri="FieldURI", is_attribute=True, is_required=True)
class CompleteName(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/completename"""
ELEMENT_NAME = "CompleteName"
title = CharField(field_uri="Title")
first_name = CharField(field_uri="FirstName")
middle_name = CharField(field_uri="MiddleName")
last_name = CharField(field_uri="LastName")
suffix = CharField(field_uri="Suffix")
initials = CharField(field_uri="Initials")
full_name = CharField(field_uri="FullName")
nickname = CharField(field_uri="Nickname")
yomi_first_name = CharField(field_uri="YomiFirstName")
yomi_last_name = CharField(field_uri="YomiLastName")
class ReminderMessageData(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/remindermessagedata"""
ELEMENT_NAME = "ReminderMessageData"
reminder_text = CharField(field_uri="ReminderText")
location = CharField(field_uri="Location")
start_time = TimeField(field_uri="StartTime")
end_time = TimeField(field_uri="EndTime")
associated_calendar_item_id = AssociatedCalendarItemIdField(
field_uri="AssociatedCalendarItemId", supported_from=Build(15, 0, 913, 9)
)
class AcceptSharingInvitation(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/acceptsharinginvitation"""
ELEMENT_NAME = "AcceptSharingInvitation"
reference_item_id = ReferenceItemIdField(field_uri="item:ReferenceItemId")
class SuppressReadReceipt(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/suppressreadreceipt"""
ELEMENT_NAME = "SuppressReadReceipt"
reference_item_id = ReferenceItemIdField(field_uri="item:ReferenceItemId")
class RemoveItem(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/removeitem"""
ELEMENT_NAME = "RemoveItem"
reference_item_id = ReferenceItemIdField(field_uri="item:ReferenceItemId")
class ResponseObjects(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/responseobjects"""
ELEMENT_NAME = "ResponseObjects"
accept_item = EWSElementField(field_uri="AcceptItem", value_cls="AcceptItem", namespace=TNS)
tentatively_accept_item = EWSElementField(
field_uri="TentativelyAcceptItem", value_cls="TentativelyAcceptItem", namespace=TNS
)
decline_item = EWSElementField(field_uri="DeclineItem", value_cls="DeclineItem", namespace=TNS)
reply_to_item = EWSElementField(field_uri="ReplyToItem", value_cls="ReplyToItem", namespace=TNS)
forward_item = EWSElementField(field_uri="ForwardItem", value_cls="ForwardItem", namespace=TNS)
reply_all_to_item = EWSElementField(field_uri="ReplyAllToItem", value_cls="ReplyAllToItem", namespace=TNS)
cancel_calendar_item = EWSElementField(
field_uri="CancelCalendarItem", value_cls="CancelCalendarItem", namespace=TNS
)
remove_item = EWSElementField(field_uri="RemoveItem", value_cls=RemoveItem)
post_reply_item = EWSElementField(field_uri="PostReplyItem", value_cls="PostReplyItem", namespace=TNS)
success_read_receipt = EWSElementField(field_uri="SuppressReadReceipt", value_cls=SuppressReadReceipt)
accept_sharing_invitation = EWSElementField(field_uri="AcceptSharingInvitation", value_cls=AcceptSharingInvitation)
class PhoneNumber(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/phonenumber"""
ELEMENT_NAME = "PhoneNumber"
number = CharField(field_uri="Number")
type = CharField(field_uri="Type")
class IdChangeKeyMixIn(EWSElement, metaclass=EWSMeta):
"""Base class for classes that have a concept of 'id' and 'changekey' values. The values are actually stored on
a separate element but we add convenience methods to hide that fact.
"""
ID_ELEMENT_CLS = None
def __init__(self, **kwargs):
_id, _changekey = kwargs.pop("id", None), kwargs.pop("changekey", None)
if _id or _changekey:
kwargs["_id"] = self.ID_ELEMENT_CLS(_id, _changekey)
super().__init__(**kwargs)
@classmethod
def get_field_by_fieldname(cls, fieldname):
if fieldname in ("id", "changekey"):
return cls.ID_ELEMENT_CLS.get_field_by_fieldname(fieldname=fieldname)
return super().get_field_by_fieldname(fieldname=fieldname)
@property
def id(self):
if self._id is None:
return None
return self._id.id
@id.setter
def id(self, value):
if self._id is None:
self._id = self.ID_ELEMENT_CLS()
self._id.id = value
@property
def changekey(self):
if self._id is None:
return None
return self._id.changekey
@changekey.setter
def changekey(self, value):
if self._id is None:
self._id = self.ID_ELEMENT_CLS()
self._id.changekey = value
@classmethod
def id_from_xml(cls, elem):
# This method must be reasonably fast
id_elem = elem.find(cls.ID_ELEMENT_CLS.response_tag())
if id_elem is None:
return None, None
return id_elem.get(cls.ID_ELEMENT_CLS.ID_ATTR), id_elem.get(cls.ID_ELEMENT_CLS.CHANGEKEY_ATTR)
def to_id(self):
if self._id is None:
raise ValueError("Must have an ID")
return self._id
def __eq__(self, other):
if isinstance(other, tuple):
return hash((self.id, self.changekey)) == hash(other)
return super().__eq__(other)
def __hash__(self):
# If we have an ID and changekey, use that as key. Else return a hash of all attributes
if self.id:
return hash((self.id, self.changekey))
return super().__hash__()
class DictionaryEntry(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/dictionaryentry"""
ELEMENT_NAME = "DictionaryEntry"
key = TypeValueField(field_uri="DictionaryKey")
value = TypeValueField(field_uri="DictionaryValue")
class UserConfigurationName(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/userconfigurationname"""
ELEMENT_NAME = "UserConfigurationName"
NAMESPACE = TNS
name = CharField(field_uri="Name", is_attribute=True)
folder = EWSElementField(value_cls=FolderId)
def clean(self, version=None):
from .folders import BaseFolder
if isinstance(self.folder, BaseFolder):
self.folder = self.folder.to_id()
super().clean(version=version)
@classmethod
def from_xml(cls, elem, account):
# We also accept distinguished folders
f = EWSElementField(value_cls=DistinguishedFolderId)
distinguished_folder_id = f.from_xml(elem=elem, account=account)
res = super().from_xml(elem=elem, account=account)
if distinguished_folder_id:
res.folder = distinguished_folder_id
return res
class UserConfigurationNameMNS(UserConfigurationName):
"""Like UserConfigurationName, but in the MNS namespace.
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/userconfigurationname
"""
NAMESPACE = MNS
class UserConfiguration(IdChangeKeyMixIn):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/userconfiguration"""
ELEMENT_NAME = "UserConfiguration"
NAMESPACE = MNS
ID_ELEMENT_CLS = ItemId
_id = IdElementField(field_uri="ItemId", value_cls=ID_ELEMENT_CLS)
user_configuration_name = EWSElementField(value_cls=UserConfigurationName)
dictionary = DictionaryField(field_uri="Dictionary")
xml_data = Base64Field(field_uri="XmlData")
binary_data = Base64Field(field_uri="BinaryData")
class Attribution(IdChangeKeyMixIn):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/phonenumber"""
ELEMENT_NAME = "Attribution"
ID_ELEMENT_CLS = SourceId
ID = CharField(field_uri="Id")
_id = IdElementField(field_uri="SourceId", value_cls=ID_ELEMENT_CLS)
display_name = CharField(field_uri="DisplayName")
is_writable = BooleanField(field_uri="IsWritable")
is_quick_contact = BooleanField(field_uri="IsQuickContact")
is_hidden = BooleanField(field_uri="IsHidden")
folder_id = EWSElementField(value_cls=FolderId)
class BodyContentValue(EWSElement):
"""MSDN:
https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/value-bodycontenttype
"""
ELEMENT_NAME = "Value"
value = CharField(field_uri="Value")
body_type = CharField(field_uri="BodyType")
class BodyContentAttributedValue(EWSElement):
"""MSDN:
https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/bodycontentattributedvalue
"""
ELEMENT_NAME = "BodyContentAttributedValue"
value = EWSElementField(value_cls=BodyContentValue)
attributions = EWSElementListField(field_uri="Attributions", value_cls=Attribution)
class StringAttributedValue(EWSElement):
"""MSDN:
https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/stringattributedvalue
"""
ELEMENT_NAME = "StringAttributedValue"
value = CharField(field_uri="Value")
attributions = CharListField(field_uri="Attributions", list_elem_name="Attribution")
class PersonaPhoneNumberTypeValue(EWSElement):
"""MSDN:
https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/value-personaphonenumbertype
"""
ELEMENT_NAME = "Value"
number = CharField(field_uri="Number")
type = CharField(field_uri="Type")
class PhoneNumberAttributedValue(EWSElement):
"""MSDN:
https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/phonenumberattributedvalue
"""
ELEMENT_NAME = "PhoneNumberAttributedValue"
value = EWSElementField(value_cls=PersonaPhoneNumberTypeValue)
attributions = CharListField(field_uri="Attributions", list_elem_name="Attribution")
class EmailAddressTypeValue(Mailbox):
"""MSDN:
https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/value-emailaddresstype
"""
ELEMENT_NAME = "Value"
original_display_name = TextField(field_uri="OriginalDisplayName")
class EmailAddressAttributedValue(EWSElement):
"""MSDN:
https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/emailaddressattributedvalue
"""
ELEMENT_NAME = "EmailAddressAttributedValue"
value = EWSElementField(value_cls=EmailAddressTypeValue)
attributions = EWSElementListField(field_uri="Attributions", value_cls=Attribution)
class PersonaPostalAddressTypeValue(Mailbox):
"""MSDN:
https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/value-personapostaladdresstype
"""
ELEMENT_NAME = "Value"
street = TextField(field_uri="Street")
city = TextField(field_uri="City")
state = TextField(field_uri="State")
country = TextField(field_uri="Country")
postal_code = TextField(field_uri="PostalCode")
post_office_box = TextField(field_uri="PostOfficeBox")
type = TextField(field_uri="Type")
latitude = TextField(field_uri="Latitude")
longitude = TextField(field_uri="Longitude")
accuracy = TextField(field_uri="Accuracy")
altitude = TextField(field_uri="Altitude")
altitude_accuracy = TextField(field_uri="AltitudeAccuracy")
formatted_address = TextField(field_uri="FormattedAddress")
location_uri = TextField(field_uri="LocationUri")
location_source = TextField(field_uri="LocationSource")
class PostalAddressAttributedValue(EWSElement):
"""MSDN:
https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/postaladdressattributedvalue
"""
ELEMENT_NAME = "PostalAddressAttributedValue"
value = EWSElementField(value_cls=PersonaPostalAddressTypeValue)
attributions = EWSElementListField(field_uri="Attributions", value_cls=Attribution)
class Event(EWSElement, metaclass=EWSMeta):
"""Base class for all event types."""
watermark = CharField(field_uri="Watermark")
class TimestampEvent(Event, metaclass=EWSMeta):
"""Base class for both item and folder events with a timestamp."""
FOLDER = "folder"
ITEM = "item"
timestamp = DateTimeField(field_uri="TimeStamp")
item_id = EWSElementField(value_cls=ItemId)
folder_id = EWSElementField(value_cls=FolderId)
parent_folder_id = EWSElementField(value_cls=ParentFolderId)
@property
def event_type(self):
if self.item_id is not None:
return self.ITEM
if self.folder_id is not None:
return self.FOLDER
return None # Empty object
class OldTimestampEvent(TimestampEvent, metaclass=EWSMeta):
"""Base class for both item and folder copy/move events."""
old_item_id = EWSElementField(value_cls=OldItemId)
old_folder_id = EWSElementField(value_cls=OldFolderId)
old_parent_folder_id = EWSElementField(value_cls=OldParentFolderId)
class CopiedEvent(OldTimestampEvent):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/copiedevent"""
ELEMENT_NAME = "CopiedEvent"
class CreatedEvent(TimestampEvent):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/createdevent"""
ELEMENT_NAME = "CreatedEvent"
class DeletedEvent(TimestampEvent):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/deletedevent"""
ELEMENT_NAME = "DeletedEvent"
class ModifiedEvent(TimestampEvent):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/modifiedevent"""
ELEMENT_NAME = "ModifiedEvent"
unread_count = IntegerField(field_uri="UnreadCount")
class MovedEvent(OldTimestampEvent):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/movedevent"""
ELEMENT_NAME = "MovedEvent"
class NewMailEvent(TimestampEvent):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/newmailevent"""
ELEMENT_NAME = "NewMailEvent"
class StatusEvent(Event):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/statusevent"""
ELEMENT_NAME = "StatusEvent"
class FreeBusyChangedEvent(TimestampEvent):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/freebusychangedevent"""
ELEMENT_NAME = "FreeBusyChangedEvent"
class Notification(EWSElement):
"""MSDN:
https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/notification-ex15websvcsotherref
"""
ELEMENT_NAME = "Notification"
NAMESPACE = MNS
subscription_id = CharField(field_uri="SubscriptionId")
previous_watermark = CharField(field_uri="PreviousWatermark")
more_events = BooleanField(field_uri="MoreEvents")
events = GenericEventListField("")
class BaseTransition(EWSElement, metaclass=EWSMeta):
"""Base class for all other transition classes"""
to = CharField(field_uri="To")
kind = CharField(field_uri="Kind", is_attribute=True) # An attribute on the 'To' element
@staticmethod
def transition_model_from_tag(tag):
return {
cls.response_tag(): cls
for cls in (Transition, AbsoluteDateTransition, RecurringDateTransition, RecurringDayTransition)
}[tag]
@classmethod
def from_xml(cls, elem, account):
kind = elem.find(cls.get_field_by_fieldname("to").response_tag()).get("Kind")
res = super().from_xml(elem=elem, account=account)
res.kind = kind
return res
class Transition(BaseTransition):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/transition"""
ELEMENT_NAME = "Transition"
class AbsoluteDateTransition(BaseTransition):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/absolutedatetransition"""
ELEMENT_NAME = "AbsoluteDateTransition"
date = DateTimeBackedDateField(field_uri="DateTime")
class RecurringDayTransition(BaseTransition):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/recurringdaytransition"""
ELEMENT_NAME = "RecurringDayTransition"
offset = TimeDeltaField(field_uri="TimeOffset")
month = IntegerField(field_uri="Month")
# Valid ISO 8601 weekday, as a number in range 1 -> 7 (1 being Monday)
day_of_week = EnumField(field_uri="DayOfWeek", enum=WEEKDAY_NAMES)
occurrence = IntegerField(field_uri="Occurrence")
@classmethod
def from_xml(cls, elem, account):
res = super().from_xml(elem, account)
# See TimeZoneTransition.from_xml()
if res.occurrence == -1:
res.occurrence = 5
return res
class RecurringDateTransition(BaseTransition):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/recurringdatetransition"""
ELEMENT_NAME = "RecurringDateTransition"
offset = TimeDeltaField(field_uri="TimeOffset")
month = IntegerField(field_uri="Month")
day = IntegerField(field_uri="Day") # Day of month
class Period(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/period"""
ELEMENT_NAME = "Period"
id = CharField(field_uri="Id", is_attribute=True)
name = CharField(field_uri="Name", is_attribute=True)
bias = TimeDeltaField(field_uri="Bias", is_attribute=True)
@property
def bias_in_minutes(self):
return int(self.bias.total_seconds()) // 60 # Convert to minutes
class TransitionsGroup(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/transitionsgroup"""
ELEMENT_NAME = "TransitionsGroup"
id = CharField(field_uri="Id", is_attribute=True)
transitions = TransitionListField(value_cls=BaseTransition)
class TimeZoneDefinition(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/timezonedefinition"""
ELEMENT_NAME = "TimeZoneDefinition"
id = CharField(field_uri="Id", is_attribute=True)
name = CharField(field_uri="Name", is_attribute=True)
periods = EWSElementListField(field_uri="Periods", value_cls=Period)
transitions_groups = EWSElementListField(field_uri="TransitionsGroups", value_cls=TransitionsGroup)
transitions = TransitionListField(field_uri="Transitions", value_cls=BaseTransition)
@classmethod
def from_xml(cls, elem, account):
return super().from_xml(elem, account)
def _get_standard_period(self, transitions_group):
# Find the first standard period referenced from transitions_group
standard_periods_map = {p.id: p for p in self.periods if p.name == "Standard"}
for transition in transitions_group.transitions:
try:
return standard_periods_map[transition.to]
except KeyError:
continue
raise ValueError(f"No standard period matching any transition in {transitions_group}")
def _get_transitions_group(self, for_year):
# Look through the transitions, and pick the relevant transition group according to the 'for_year' value
transitions_group = None
transitions_groups_map = {tg.id: tg for tg in self.transitions_groups}
for transition in sorted(self.transitions, key=lambda t: t.to):
if transition.kind != "Group":
continue
if isinstance(transition, AbsoluteDateTransition) and transition.date.year > for_year:
break
transitions_group = transitions_groups_map[transition.to]
if transitions_group is None:
raise ValueError(f"No valid transition group for year {for_year}: {self.transitions}")
return transitions_group
def get_std_and_dst(self, for_year):
# Return 'standard_time' and 'daylight_time' objects. We do unnecessary work here, but it keeps code simple.
transitions_group = self._get_transitions_group(for_year)
if not 0 <= len(transitions_group.transitions) <= 2:
raise ValueError(f"Expected 0-2 transitions in transitions group {transitions_group}")
standard_period = self._get_standard_period(transitions_group)
periods_map = {p.id: p for p in self.periods}
standard_time, daylight_time = None, None
if len(transitions_group.transitions) == 1:
# This is a simple transition group representing a timezone with no DST. Some servers don't accept
# TimeZone elements without a STD and DST element (see issue #488). Return StandardTime and DaylightTime
# objects with dummy values and 0 bias - this satisfies the broken servers and hopefully doesn't break
# the well-behaving servers.
standard_time = StandardTime(bias=0, time=datetime.time(0), occurrence=1, iso_month=1, weekday=1)
daylight_time = DaylightTime(bias=0, time=datetime.time(0), occurrence=5, iso_month=12, weekday=7)
return standard_time, daylight_time, standard_period
for transition in transitions_group.transitions:
# 'offset' is the time of day to transition, as timedelta since midnight. Check that it's a reasonable value
transition.clean(version=None)
transition_kwargs = dict(
time=(datetime.datetime(2000, 1, 1) + transition.offset).time(),
occurrence=transition.occurrence,
iso_month=transition.month,
weekday=transition.day_of_week,
)
period = periods_map[transition.to]
if period.name == "Standard":
transition_kwargs["bias"] = 0
standard_time = StandardTime(**transition_kwargs)
continue
if period.name == "Daylight":
transition_kwargs["bias"] = period.bias_in_minutes - standard_period.bias_in_minutes
daylight_time = DaylightTime(**transition_kwargs)
continue
raise ValueError(f"Unknown transition: {transition}")
return standard_time, daylight_time, standard_period
Classes
class AbsoluteDateTransition (**kwargs)
-
Expand source code
class AbsoluteDateTransition(BaseTransition): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/absolutedatetransition""" ELEMENT_NAME = "AbsoluteDateTransition" date = DateTimeBackedDateField(field_uri="DateTime")
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var date
Inherited members
class AcceptSharingInvitation (**kwargs)
-
Expand source code
class AcceptSharingInvitation(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/acceptsharinginvitation""" ELEMENT_NAME = "AcceptSharingInvitation" reference_item_id = ReferenceItemIdField(field_uri="item:ReferenceItemId")
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var reference_item_id
Inherited members
class Address (**kwargs)
-
Like Mailbox, but with a different tag name.
Expand source code
class Address(Mailbox): """Like Mailbox, but with a different tag name. MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/address-emailaddresstype """ ELEMENT_NAME = "Address"
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class AlternateId (**kwargs)
-
Expand source code
class AlternateId(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/alternateid""" ELEMENT_NAME = "AlternateId" id = CharField(field_uri="Id", is_required=True, is_attribute=True) format = ChoiceField( field_uri="Format", is_required=True, is_attribute=True, choices={Choice(c) for c in ID_FORMATS} ) mailbox = EmailAddressField(field_uri="Mailbox", is_required=True, is_attribute=True) is_archive = BooleanField(field_uri="IsArchive", is_required=False, is_attribute=True) @classmethod def response_tag(cls): # This element is in TNS in the request and MNS in the response... return f"{{{MNS}}}{cls.ELEMENT_NAME}"
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Static methods
def response_tag()
-
Expand source code
@classmethod def response_tag(cls): # This element is in TNS in the request and MNS in the response... return f"{{{MNS}}}{cls.ELEMENT_NAME}"
Instance variables
var format
var id
var is_archive
var mailbox
Inherited members
class AlternatePublicFolderId (**kwargs)
-
Expand source code
class AlternatePublicFolderId(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/alternatepublicfolderid""" ELEMENT_NAME = "AlternatePublicFolderId" folder_id = CharField(field_uri="FolderId", is_required=True, is_attribute=True) format = ChoiceField( field_uri="Format", is_required=True, is_attribute=True, choices={Choice(c) for c in ID_FORMATS} )
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var folder_id
var format
Inherited members
class AlternatePublicFolderItemId (**kwargs)
-
Expand source code
class AlternatePublicFolderItemId(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/alternatepublicfolderitemid """ ELEMENT_NAME = "AlternatePublicFolderItemId" folder_id = CharField(field_uri="FolderId", is_required=True, is_attribute=True) format = ChoiceField( field_uri="Format", is_required=True, is_attribute=True, choices={Choice(c) for c in ID_FORMATS} ) item_id = CharField(field_uri="ItemId", is_required=True, is_attribute=True)
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var folder_id
var format
var item_id
Inherited members
class AssociatedCalendarItemId (*args, **kwargs)
-
Expand source code
class AssociatedCalendarItemId(ItemId): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/associatedcalendaritemid """ ELEMENT_NAME = "AssociatedCalendarItemId"
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class Attendee (**kwargs)
-
Expand source code
class Attendee(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/attendee""" ELEMENT_NAME = "Attendee" RESPONSE_TYPES = {"Unknown", "Organizer", "Tentative", "Accept", "Decline", "NoResponseReceived"} mailbox = MailboxField(is_required=True) response_type = ChoiceField( field_uri="ResponseType", choices={Choice(c) for c in RESPONSE_TYPES}, default="Unknown" ) last_response_time = DateTimeField(field_uri="LastResponseTime") def __hash__(self): return hash(self.mailbox)
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
var RESPONSE_TYPES
Instance variables
var last_response_time
var mailbox
var response_type
Inherited members
class Attribution (**kwargs)
-
Expand source code
class Attribution(IdChangeKeyMixIn): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/phonenumber""" ELEMENT_NAME = "Attribution" ID_ELEMENT_CLS = SourceId ID = CharField(field_uri="Id") _id = IdElementField(field_uri="SourceId", value_cls=ID_ELEMENT_CLS) display_name = CharField(field_uri="DisplayName") is_writable = BooleanField(field_uri="IsWritable") is_quick_contact = BooleanField(field_uri="IsQuickContact") is_hidden = BooleanField(field_uri="IsHidden") folder_id = EWSElementField(value_cls=FolderId)
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
var ID_ELEMENT_CLS
Instance variables
var ID
var display_name
var folder_id
var is_quick_contact
var is_writable
Inherited members
class AvailabilityMailbox (**kwargs)
-
Like Mailbox, but with slightly different attributes.
Expand source code
class AvailabilityMailbox(EWSElement): """Like Mailbox, but with slightly different attributes. MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/mailbox-availability """ ELEMENT_NAME = "Mailbox" name = TextField(field_uri="Name") email_address = EmailAddressField(field_uri="Address", is_required=True) # RoutingType values restricted to EX and SMTP: # https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/routingtype-emailaddress routing_type = RoutingTypeField(field_uri="RoutingType") def __hash__(self): # Exchange may add 'name' on insert. We're satisfied if the email address matches. if self.email_address: return hash(self.email_address.lower()) return super().__hash__() @classmethod def from_mailbox(cls, mailbox): if not isinstance(mailbox, Mailbox): raise InvalidTypeError("mailbox", mailbox, Mailbox) return cls(name=mailbox.name, email_address=mailbox.email_address, routing_type=mailbox.routing_type)
Ancestors
Subclasses
Class variables
var ELEMENT_NAME
var FIELDS
Static methods
def from_mailbox(mailbox)
-
Expand source code
@classmethod def from_mailbox(cls, mailbox): if not isinstance(mailbox, Mailbox): raise InvalidTypeError("mailbox", mailbox, Mailbox) return cls(name=mailbox.name, email_address=mailbox.email_address, routing_type=mailbox.routing_type)
Instance variables
var email_address
var name
var routing_type
Inherited members
class BaseItemId (*args, **kwargs)
-
Base class for ItemId elements.
Expand source code
class BaseItemId(EWSElement, metaclass=EWSMeta): """Base class for ItemId elements.""" ID_ATTR = None CHANGEKEY_ATTR = None def __init__(self, *args, **kwargs): if not kwargs: # Allow to set attributes without keyword kwargs = dict(zip(self._slots_keys, args)) super().__init__(**kwargs)
Ancestors
Subclasses
Class variables
var CHANGEKEY_ATTR
var ID_ATTR
Inherited members
class BasePermission (**kwargs)
-
Base class for the Permission and CalendarPermission classes
Expand source code
class BasePermission(EWSElement, metaclass=EWSMeta): """Base class for the Permission and CalendarPermission classes""" PERMISSION_ENUM = {Choice("None"), Choice("Owned"), Choice("All")} can_create_items = BooleanField(field_uri="CanCreateItems", default=False) can_create_subfolders = BooleanField(field_uri="CanCreateSubfolders", default=False) is_folder_owner = BooleanField(field_uri="IsFolderOwner", default=False) is_folder_visible = BooleanField(field_uri="IsFolderVisible", default=False) is_folder_contact = BooleanField(field_uri="IsFolderContact", default=False) edit_items = ChoiceField(field_uri="EditItems", choices=PERMISSION_ENUM, default="None") delete_items = ChoiceField(field_uri="DeleteItems", choices=PERMISSION_ENUM, default="None") read_items = ChoiceField(field_uri="ReadItems", choices={Choice("None"), Choice("FullDetails")}, default="None") user_id = EWSElementField(value_cls=UserId, is_required=True)
Ancestors
Subclasses
Class variables
var FIELDS
var PERMISSION_ENUM
Instance variables
var can_create_items
var can_create_subfolders
var delete_items
var edit_items
var is_folder_contact
var is_folder_owner
var is_folder_visible
var read_items
var user_id
Inherited members
class BaseTransition (**kwargs)
-
Base class for all other transition classes
Expand source code
class BaseTransition(EWSElement, metaclass=EWSMeta): """Base class for all other transition classes""" to = CharField(field_uri="To") kind = CharField(field_uri="Kind", is_attribute=True) # An attribute on the 'To' element @staticmethod def transition_model_from_tag(tag): return { cls.response_tag(): cls for cls in (Transition, AbsoluteDateTransition, RecurringDateTransition, RecurringDayTransition) }[tag] @classmethod def from_xml(cls, elem, account): kind = elem.find(cls.get_field_by_fieldname("to").response_tag()).get("Kind") res = super().from_xml(elem=elem, account=account) res.kind = kind return res
Ancestors
Subclasses
Class variables
var FIELDS
Static methods
def from_xml(elem, account)
-
Expand source code
@classmethod def from_xml(cls, elem, account): kind = elem.find(cls.get_field_by_fieldname("to").response_tag()).get("Kind") res = super().from_xml(elem=elem, account=account) res.kind = kind return res
def transition_model_from_tag(tag)
-
Expand source code
@staticmethod def transition_model_from_tag(tag): return { cls.response_tag(): cls for cls in (Transition, AbsoluteDateTransition, RecurringDateTransition, RecurringDayTransition) }[tag]
Instance variables
var kind
var to
Inherited members
class Body (...)
-
Helper to mark the 'body' field as a complex attribute.
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/body
Expand source code
class Body(str): """Helper to mark the 'body' field as a complex attribute. MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/body """ body_type = "Text" def __add__(self, other): # Make sure Body('') + 'foo' returns a Body type return self.__class__(super().__add__(other)) def __mod__(self, other): # Make sure Body('%s') % 'foo' returns a Body type return self.__class__(super().__mod__(other)) def format(self, *args, **kwargs): # Make sure Body('{}').format('foo') returns a Body type return self.__class__(super().format(*args, **kwargs))
Ancestors
- builtins.str
Subclasses
Class variables
var body_type
Methods
def format(self, *args, **kwargs)
-
S.format(args, *kwargs) -> str
Return a formatted version of S, using substitutions from args and kwargs. The substitutions are identified by braces ('{' and '}').
Expand source code
def format(self, *args, **kwargs): # Make sure Body('{}').format('foo') returns a Body type return self.__class__(super().format(*args, **kwargs))
class BodyContentAttributedValue (**kwargs)
-
Expand source code
class BodyContentAttributedValue(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/bodycontentattributedvalue """ ELEMENT_NAME = "BodyContentAttributedValue" value = EWSElementField(value_cls=BodyContentValue) attributions = EWSElementListField(field_uri="Attributions", value_cls=Attribution)
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var attributions
var value
Inherited members
class BodyContentValue (**kwargs)
-
Expand source code
class BodyContentValue(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/value-bodycontenttype """ ELEMENT_NAME = "Value" value = CharField(field_uri="Value") body_type = CharField(field_uri="BodyType")
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var body_type
var value
Inherited members
class CalendarEvent (**kwargs)
-
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/calendarevent
Expand source code
class CalendarEvent(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/calendarevent""" ELEMENT_NAME = "CalendarEvent" start = DateTimeField(field_uri="StartTime") end = DateTimeField(field_uri="EndTime") busy_type = FreeBusyStatusField(field_uri="BusyType", is_required=True, default="Busy") details = EWSElementField(value_cls=CalendarEventDetails)
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var busy_type
var details
var end
var start
Inherited members
class CalendarEventDetails (**kwargs)
-
Expand source code
class CalendarEventDetails(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/calendareventdetails""" ELEMENT_NAME = "CalendarEventDetails" id = CharField(field_uri="ID") subject = CharField(field_uri="Subject") location = CharField(field_uri="Location") is_meeting = BooleanField(field_uri="IsMeeting") is_recurring = BooleanField(field_uri="IsRecurring") is_exception = BooleanField(field_uri="IsException") is_reminder_set = BooleanField(field_uri="IsReminderSet") is_private = BooleanField(field_uri="IsPrivate")
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var id
var is_exception
var is_meeting
var is_private
var is_recurring
var is_reminder_set
var location
var subject
Inherited members
class CalendarPermission (**kwargs)
-
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/calendarpermission
Expand source code
class CalendarPermission(BasePermission): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/calendarpermission""" ELEMENT_NAME = "CalendarPermission" LEVEL_CHOICES = ( "None", "Owner", "PublishingEditor", "Editor", "PublishingAuthor", "Author", "NoneditingAuthor", "Reviewer", "Contributor", "FreeBusyTimeOnly", "FreeBusyTimeAndSubjectAndLocation", "Custom", ) calendar_permission_level = ChoiceField( field_uri="CalendarPermissionLevel", choices={Choice(c) for c in LEVEL_CHOICES}, default=LEVEL_CHOICES[0] )
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
var LEVEL_CHOICES
Instance variables
var calendar_permission_level
Inherited members
class CalendarView (**kwargs)
-
Expand source code
class CalendarView(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/calendarview""" ELEMENT_NAME = "CalendarView" NAMESPACE = MNS start = DateTimeField(field_uri="StartDate", is_required=True, is_attribute=True) end = DateTimeField(field_uri="EndDate", is_required=True, is_attribute=True) max_items = IntegerField(field_uri="MaxEntriesReturned", min=1, is_attribute=True) def clean(self, version=None): super().clean(version=version) if self.end < self.start: raise ValueError("'start' must be before 'end'")
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
var NAMESPACE
Instance variables
var end
var max_items
var start
Methods
def clean(self, version=None)
-
Expand source code
def clean(self, version=None): super().clean(version=version) if self.end < self.start: raise ValueError("'start' must be before 'end'")
Inherited members
class CompleteName (**kwargs)
-
Expand source code
class CompleteName(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/completename""" ELEMENT_NAME = "CompleteName" title = CharField(field_uri="Title") first_name = CharField(field_uri="FirstName") middle_name = CharField(field_uri="MiddleName") last_name = CharField(field_uri="LastName") suffix = CharField(field_uri="Suffix") initials = CharField(field_uri="Initials") full_name = CharField(field_uri="FullName") nickname = CharField(field_uri="Nickname") yomi_first_name = CharField(field_uri="YomiFirstName") yomi_last_name = CharField(field_uri="YomiLastName")
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var first_name
var full_name
var initials
var last_name
var middle_name
var nickname
var suffix
var title
var yomi_first_name
var yomi_last_name
Inherited members
class ConversationId (*args, **kwargs)
-
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/conversationid
Expand source code
class ConversationId(ItemId): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/conversationid""" ELEMENT_NAME = "ConversationId" # ChangeKey attribute is sometimes required, see MSDN link
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class CopiedEvent (**kwargs)
-
Expand source code
class CopiedEvent(OldTimestampEvent): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/copiedevent""" ELEMENT_NAME = "CopiedEvent"
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class CreatedEvent (**kwargs)
-
Expand source code
class CreatedEvent(TimestampEvent): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/createdevent""" ELEMENT_NAME = "CreatedEvent"
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class DLMailbox (**kwargs)
-
Like Mailbox, but creates elements in the 'messages' namespace when sending requests.
Expand source code
class DLMailbox(Mailbox): """Like Mailbox, but creates elements in the 'messages' namespace when sending requests.""" NAMESPACE = MNS
Ancestors
Class variables
var NAMESPACE
Inherited members
class DaylightTime (**kwargs)
-
Expand source code
class DaylightTime(TimeZoneTransition): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/daylighttime""" ELEMENT_NAME = "DaylightTime"
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class DelegatePermissions (**kwargs)
-
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/delegatepermissions
Expand source code
class DelegatePermissions(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/delegatepermissions""" ELEMENT_NAME = "DelegatePermissions" PERMISSION_LEVEL_CHOICES = { Choice("None"), Choice("Editor"), Choice("Reviewer"), Choice("Author"), Choice("Custom"), } calendar_folder_permission_level = ChoiceField( field_uri="CalendarFolderPermissionLevel", choices=PERMISSION_LEVEL_CHOICES, default="None" ) tasks_folder_permission_level = ChoiceField( field_uri="TasksFolderPermissionLevel", choices=PERMISSION_LEVEL_CHOICES, default="None" ) inbox_folder_permission_level = ChoiceField( field_uri="InboxFolderPermissionLevel", choices=PERMISSION_LEVEL_CHOICES, default="None" ) contacts_folder_permission_level = ChoiceField( field_uri="ContactsFolderPermissionLevel", choices=PERMISSION_LEVEL_CHOICES, default="None" ) notes_folder_permission_level = ChoiceField( field_uri="NotesFolderPermissionLevel", choices=PERMISSION_LEVEL_CHOICES, default="None" ) journal_folder_permission_level = ChoiceField( field_uri="JournalFolderPermissionLevel", choices=PERMISSION_LEVEL_CHOICES, default="None" )
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
var PERMISSION_LEVEL_CHOICES
Instance variables
var calendar_folder_permission_level
var contacts_folder_permission_level
var inbox_folder_permission_level
var journal_folder_permission_level
var notes_folder_permission_level
var tasks_folder_permission_level
Inherited members
class DelegateUser (**kwargs)
-
Expand source code
class DelegateUser(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/delegateuser""" ELEMENT_NAME = "DelegateUser" NAMESPACE = MNS user_id = EWSElementField(value_cls=UserId) delegate_permissions = EWSElementField(value_cls=DelegatePermissions) receive_copies_of_meeting_messages = BooleanField(field_uri="ReceiveCopiesOfMeetingMessages", default=False) view_private_items = BooleanField(field_uri="ViewPrivateItems", default=False)
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
var NAMESPACE
Instance variables
var delegate_permissions
var receive_copies_of_meeting_messages
var user_id
var view_private_items
Inherited members
class DeletedEvent (**kwargs)
-
Expand source code
class DeletedEvent(TimestampEvent): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/deletedevent""" ELEMENT_NAME = "DeletedEvent"
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class DictionaryEntry (**kwargs)
-
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/dictionaryentry
Expand source code
class DictionaryEntry(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/dictionaryentry""" ELEMENT_NAME = "DictionaryEntry" key = TypeValueField(field_uri="DictionaryKey") value = TypeValueField(field_uri="DictionaryValue")
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var key
var value
Inherited members
class DistinguishedFolderId (*args, **kwargs)
-
Expand source code
class DistinguishedFolderId(FolderId): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/distinguishedfolderid""" ELEMENT_NAME = "DistinguishedFolderId" mailbox = MailboxField() def clean(self, version=None): from .folders import PublicFoldersRoot super().clean(version=version) if self.id == PublicFoldersRoot.DISTINGUISHED_FOLDER_ID: # Avoid "ErrorInvalidOperation: It is not valid to specify a mailbox with the public folder root" from EWS self.mailbox = None
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var mailbox
Methods
def clean(self, version=None)
-
Expand source code
def clean(self, version=None): from .folders import PublicFoldersRoot super().clean(version=version) if self.id == PublicFoldersRoot.DISTINGUISHED_FOLDER_ID: # Avoid "ErrorInvalidOperation: It is not valid to specify a mailbox with the public folder root" from EWS self.mailbox = None
Inherited members
class EWSElement (**kwargs)
-
Base class for all XML element implementations.
Expand source code
class EWSElement(metaclass=EWSMeta): """Base class for all XML element implementations.""" ELEMENT_NAME = None # The name of the XML tag FIELDS = Fields() # A list of attributes supported by this item class, ordered the same way as in EWS documentation NAMESPACE = TNS # The XML tag namespace. Either TNS or MNS _fields_lock = Lock() def __init__(self, **kwargs): for f in self.FIELDS: setattr(self, f.name, kwargs.pop(f.name, None)) if kwargs: raise AttributeError(f"{sorted(kwargs.keys())!r} are invalid kwargs for this class") def __setattr__(self, key, value): # Avoid silently accepting spelling errors to field names that are not set via __init__. We need to be able to # set values for predefined and registered fields, whatever non-field attributes this class defines, and # property setters. if key in self.FIELDS: return super().__setattr__(key, value) if key in self._slots_keys: return super().__setattr__(key, value) if hasattr(self, key): # Property setters return super().__setattr__(key, value) raise AttributeError( f"{key!r} is not a valid attribute. See {self.__class__.__name__}.FIELDS for valid field names" ) def clean(self, version=None): # Validate attribute values using the field validator for f in self.FIELDS: if version and not f.supports_version(version): continue if isinstance(f, ExtendedPropertyField) and not hasattr(self, f.name): # The extended field may have been registered after this item was created. Set default values. setattr(self, f.name, f.clean(None, version=version)) continue val = getattr(self, f.name) setattr(self, f.name, f.clean(val, version=version)) @staticmethod def _clear(elem): # Clears an XML element to reduce memory consumption elem.clear() # Don't attempt to clean up previous siblings. We may not have parsed them yet. parent = elem.getparent() if parent is None: return parent.remove(elem) @classmethod def from_xml(cls, elem, account): kwargs = {f.name: f.from_xml(elem=elem, account=account) for f in cls.FIELDS} cls._clear(elem) return cls(**kwargs) def to_xml(self, version): self.clean(version=version) # WARNING: The order of addition of XML elements is VERY important. Exchange expects XML elements in a # specific, non-documented order and will fail with meaningless errors if the order is wrong. # Collect attributes attrs = {} for f in self.attribute_fields(): if f.is_read_only: continue value = getattr(self, f.name) if value is None or (f.is_list and not value): continue attrs[f.field_uri] = value_to_xml_text(getattr(self, f.name)) # Create element with attributes elem = create_element(self.request_tag(), attrs=attrs) # Add elements and values for f in self.supported_fields(version=version): if f.is_read_only: continue value = getattr(self, f.name) if value is None or (f.is_list and not value): continue set_xml_value(elem, f.to_xml(value, version=version)) return elem @classmethod def request_tag(cls): if not cls.ELEMENT_NAME: raise ValueError(f"Class {cls} is missing the ELEMENT_NAME attribute") return { TNS: f"t:{cls.ELEMENT_NAME}", MNS: f"m:{cls.ELEMENT_NAME}", }[cls.NAMESPACE] @classmethod def response_tag(cls): if not cls.NAMESPACE: raise ValueError(f"Class {cls} is missing the NAMESPACE attribute") if not cls.ELEMENT_NAME: raise ValueError(f"Class {cls} is missing the ELEMENT_NAME attribute") return f"{{{cls.NAMESPACE}}}{cls.ELEMENT_NAME}" @classmethod def attribute_fields(cls): return tuple(f for f in cls.FIELDS if f.is_attribute) @classmethod def supported_fields(cls, version): """Return the fields supported by the given server version.""" return tuple(f for f in cls.FIELDS if not f.is_attribute and f.supports_version(version)) @classmethod def get_field_by_fieldname(cls, fieldname): try: return cls.FIELDS[fieldname] except KeyError: raise InvalidField(f"{fieldname!r} is not a valid field name on {cls.__name__}") @classmethod def validate_field(cls, field, version): """Take a list of fieldnames, Field or FieldPath objects pointing to item fields, and check that they are valid for the given version. :param field: :param version: """ # Allow both Field and FieldPath instances and string field paths as input if isinstance(field, str): field = cls.get_field_by_fieldname(fieldname=field) elif isinstance(field, FieldPath): field = field.field cls.get_field_by_fieldname(fieldname=field.name) # Will raise if field name is invalid if not field.supports_version(version): # The field exists but is not valid for this version raise InvalidFieldForVersion( f"Field {field.name!r} is not supported on server version {version} " f"(supported from: {field.supported_from}, deprecated from: {field.deprecated_from})" ) @classmethod def add_field(cls, field, insert_after): """Insert a new field at the preferred place in the tuple and update the slots cache. :param field: :param insert_after: """ with cls._fields_lock: idx = cls.FIELDS.index_by_name(insert_after) + 1 # This class may not have its own FIELDS attribute. Make sure not to edit an attribute belonging to a parent # class. cls.FIELDS.insert(idx, field) setattr(cls, _mangle(field.name), field) @classmethod def remove_field(cls, field): """Remove the given field and and update the slots cache. :param field: """ with cls._fields_lock: # This class may not have its own FIELDS attribute. Make sure not to edit an attribute belonging to a parent # class. cls.FIELDS.remove(field) delattr(cls, _mangle(field.name)) def __eq__(self, other): return hash(self) == hash(other) def __hash__(self): return hash( tuple(tuple(getattr(self, f.name) or ()) if f.is_list else getattr(self, f.name) for f in self.FIELDS) ) def _field_vals(self): field_vals = [] # Keep sorting for f in self.FIELDS: val = getattr(self, f.name) if isinstance(f, EnumField) and isinstance(val, int): val = f.as_string(val) field_vals.append((f.name, val)) return field_vals def __str__(self): args_str = ", ".join(f"{name}={val!r}" for name, val in self._field_vals() if val is not None) return f"{self.__class__.__name__}({args_str})" def __repr__(self): args_str = ", ".join(f"{name}={val!r}" for name, val in self._field_vals()) return f"{self.__class__.__name__}({args_str})"
Subclasses
- Attachment
- AttachmentId
- Autodiscover
- AutodiscoverBase
- Error
- ErrorResponse
- ExtendedProperty
- IndexedElement
- BaseReplyItem
- AcceptSharingInvitation
- AlternateId
- AlternatePublicFolderId
- AlternatePublicFolderItemId
- Attendee
- AvailabilityMailbox
- BaseItemId
- BasePermission
- BaseTransition
- BodyContentAttributedValue
- BodyContentValue
- CalendarEvent
- CalendarEventDetails
- CalendarView
- CompleteName
- DelegatePermissions
- DelegateUser
- DictionaryEntry
- EffectiveRights
- EmailAddressAttributedValue
- Event
- ExceptionFieldURI
- ExtendedFieldURI
- FailedMailbox
- FieldURI
- FreeBusyView
- FreeBusyViewOptions
- IdChangeKeyMixIn
- IndexedFieldURI
- MailTips
- Mailbox
- MailboxData
- Member
- MessageHeader
- Notification
- OutOfOffice
- Period
- PermissionSet
- PersonaPhoneNumberTypeValue
- PhoneNumber
- PhoneNumberAttributedValue
- PostalAddressAttributedValue
- ReminderMessageData
- RemoveItem
- ResponseObjects
- SearchableMailbox
- StringAttributedValue
- SuppressReadReceipt
- TimeWindow
- TimeZone
- TimeZoneDefinition
- TimeZoneTransition
- TransitionsGroup
- UserConfigurationName
- UserId
- WorkingPeriod
- Boundary
- DeletedOccurrence
- Pattern
- Recurrence
- OofSettings
Class variables
var ELEMENT_NAME
var FIELDS
var NAMESPACE
Static methods
def add_field(field, insert_after)
-
Insert a new field at the preferred place in the tuple and update the slots cache.
:param field: :param insert_after:
Expand source code
@classmethod def add_field(cls, field, insert_after): """Insert a new field at the preferred place in the tuple and update the slots cache. :param field: :param insert_after: """ with cls._fields_lock: idx = cls.FIELDS.index_by_name(insert_after) + 1 # This class may not have its own FIELDS attribute. Make sure not to edit an attribute belonging to a parent # class. cls.FIELDS.insert(idx, field) setattr(cls, _mangle(field.name), field)
def attribute_fields()
-
Expand source code
@classmethod def attribute_fields(cls): return tuple(f for f in cls.FIELDS if f.is_attribute)
def from_xml(elem, account)
-
Expand source code
@classmethod def from_xml(cls, elem, account): kwargs = {f.name: f.from_xml(elem=elem, account=account) for f in cls.FIELDS} cls._clear(elem) return cls(**kwargs)
def get_field_by_fieldname(fieldname)
-
Expand source code
@classmethod def get_field_by_fieldname(cls, fieldname): try: return cls.FIELDS[fieldname] except KeyError: raise InvalidField(f"{fieldname!r} is not a valid field name on {cls.__name__}")
def remove_field(field)
-
Remove the given field and and update the slots cache.
:param field:
Expand source code
@classmethod def remove_field(cls, field): """Remove the given field and and update the slots cache. :param field: """ with cls._fields_lock: # This class may not have its own FIELDS attribute. Make sure not to edit an attribute belonging to a parent # class. cls.FIELDS.remove(field) delattr(cls, _mangle(field.name))
def request_tag()
-
Expand source code
@classmethod def request_tag(cls): if not cls.ELEMENT_NAME: raise ValueError(f"Class {cls} is missing the ELEMENT_NAME attribute") return { TNS: f"t:{cls.ELEMENT_NAME}", MNS: f"m:{cls.ELEMENT_NAME}", }[cls.NAMESPACE]
def response_tag()
-
Expand source code
@classmethod def response_tag(cls): if not cls.NAMESPACE: raise ValueError(f"Class {cls} is missing the NAMESPACE attribute") if not cls.ELEMENT_NAME: raise ValueError(f"Class {cls} is missing the ELEMENT_NAME attribute") return f"{{{cls.NAMESPACE}}}{cls.ELEMENT_NAME}"
def supported_fields(version)
-
Return the fields supported by the given server version.
Expand source code
@classmethod def supported_fields(cls, version): """Return the fields supported by the given server version.""" return tuple(f for f in cls.FIELDS if not f.is_attribute and f.supports_version(version))
def validate_field(field, version)
-
Take a list of fieldnames, Field or FieldPath objects pointing to item fields, and check that they are valid for the given version.
:param field: :param version:
Expand source code
@classmethod def validate_field(cls, field, version): """Take a list of fieldnames, Field or FieldPath objects pointing to item fields, and check that they are valid for the given version. :param field: :param version: """ # Allow both Field and FieldPath instances and string field paths as input if isinstance(field, str): field = cls.get_field_by_fieldname(fieldname=field) elif isinstance(field, FieldPath): field = field.field cls.get_field_by_fieldname(fieldname=field.name) # Will raise if field name is invalid if not field.supports_version(version): # The field exists but is not valid for this version raise InvalidFieldForVersion( f"Field {field.name!r} is not supported on server version {version} " f"(supported from: {field.supported_from}, deprecated from: {field.deprecated_from})" )
Methods
def clean(self, version=None)
-
Expand source code
def clean(self, version=None): # Validate attribute values using the field validator for f in self.FIELDS: if version and not f.supports_version(version): continue if isinstance(f, ExtendedPropertyField) and not hasattr(self, f.name): # The extended field may have been registered after this item was created. Set default values. setattr(self, f.name, f.clean(None, version=version)) continue val = getattr(self, f.name) setattr(self, f.name, f.clean(val, version=version))
def to_xml(self, version)
-
Expand source code
def to_xml(self, version): self.clean(version=version) # WARNING: The order of addition of XML elements is VERY important. Exchange expects XML elements in a # specific, non-documented order and will fail with meaningless errors if the order is wrong. # Collect attributes attrs = {} for f in self.attribute_fields(): if f.is_read_only: continue value = getattr(self, f.name) if value is None or (f.is_list and not value): continue attrs[f.field_uri] = value_to_xml_text(getattr(self, f.name)) # Create element with attributes elem = create_element(self.request_tag(), attrs=attrs) # Add elements and values for f in self.supported_fields(version=version): if f.is_read_only: continue value = getattr(self, f.name) if value is None or (f.is_list and not value): continue set_xml_value(elem, f.to_xml(value, version=version)) return elem
class EWSMeta (*args, **kwargs)
-
type(object) -> the object's type type(name, bases, dict, **kwds) -> a new type
Expand source code
class EWSMeta(type, metaclass=abc.ABCMeta): def __new__(mcs, name, bases, kwargs): # Collect fields defined directly on the class local_fields = Fields() for k in tuple(kwargs.keys()): v = kwargs[k] if isinstance(v, Field): v.name = k local_fields.append(v) del kwargs[k] # Build a list of fields defined on this and all base classes base_fields = Fields() for base in bases: if hasattr(base, "FIELDS"): base_fields += base.FIELDS # FIELDS defined on a model overrides the base class fields fields = kwargs.get("FIELDS", base_fields) + local_fields # Include all fields as class attributes so we can use them as instance attributes kwargs.update({_mangle(f.name): f for f in fields}) # Calculate __slots__ so we don't have to hard-code it on the model kwargs["__slots__"] = tuple(f.name for f in fields if f.name not in base_fields) + kwargs.get("__slots__", ()) # FIELDS is mentioned in docs and expected by internal code. Add it here, but only if the class has its own # fields. Otherwise, we want the implicit FIELDS from the base class (used for injecting custom fields on the # Folder class, making the custom field available for subclasses). if local_fields: kwargs["FIELDS"] = fields klass = super().__new__(mcs, name, bases, kwargs) klass._slots_keys = mcs._get_slots_keys(klass) return klass @staticmethod def _get_slots_keys(klass): seen = set() keys = [] for c in reversed(getmro(klass)): if not hasattr(c, "__slots__"): continue for k in c.__slots__: if k in seen: # We allow duplicate keys because we don't want to require subclasses of e.g. # ExtendedProperty to define an empty __slots__ class attribute. continue keys.append(k) seen.add(k) return keys def __getattribute__(cls, k): """Return Field instances via their mangled class attribute""" try: return super().__getattribute__("__dict__")[_mangle(k)] except KeyError: return super().__getattribute__(k)
Ancestors
- builtins.type
class EffectiveRights (**kwargs)
-
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/effectiverights
Expand source code
class EffectiveRights(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/effectiverights""" ELEMENT_NAME = "EffectiveRights" create_associated = BooleanField(field_uri="CreateAssociated", default=False) create_contents = BooleanField(field_uri="CreateContents", default=False) create_hierarchy = BooleanField(field_uri="CreateHierarchy", default=False) delete = BooleanField(field_uri="Delete", default=False) modify = BooleanField(field_uri="Modify", default=False) read = BooleanField(field_uri="Read", default=False) view_private_items = BooleanField(field_uri="ViewPrivateItems", default=False) def __contains__(self, item): return getattr(self, item, False)
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var create_associated
var create_contents
var create_hierarchy
var delete
var modify
var read
var view_private_items
Inherited members
class Email (**kwargs)
-
Like AvailabilityMailbox, but with a different tag name. MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/email-emailaddresstype
Expand source code
class Email(AvailabilityMailbox): """Like AvailabilityMailbox, but with a different tag name. MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/email-emailaddresstype """ ELEMENT_NAME = "Email"
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class EmailAddress (**kwargs)
-
Like Mailbox, but with a different tag name.
Expand source code
class EmailAddress(Mailbox): """Like Mailbox, but with a different tag name. MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/emailaddress-emailaddresstype """ ELEMENT_NAME = "EmailAddress"
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class EmailAddressAttributedValue (**kwargs)
-
Expand source code
class EmailAddressAttributedValue(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/emailaddressattributedvalue """ ELEMENT_NAME = "EmailAddressAttributedValue" value = EWSElementField(value_cls=EmailAddressTypeValue) attributions = EWSElementListField(field_uri="Attributions", value_cls=Attribution)
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var attributions
var value
Inherited members
class EmailAddressTypeValue (**kwargs)
-
Expand source code
class EmailAddressTypeValue(Mailbox): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/value-emailaddresstype """ ELEMENT_NAME = "Value" original_display_name = TextField(field_uri="OriginalDisplayName")
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var original_display_name
Inherited members
class Event (**kwargs)
-
Base class for all event types.
Expand source code
class Event(EWSElement, metaclass=EWSMeta): """Base class for all event types.""" watermark = CharField(field_uri="Watermark")
Ancestors
Subclasses
Class variables
var FIELDS
Instance variables
var watermark
Inherited members
class ExceptionFieldURI (**kwargs)
-
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/exceptionfielduri
Expand source code
class ExceptionFieldURI(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/exceptionfielduri""" ELEMENT_NAME = "ExceptionFieldURI" field_uri = CharField(field_uri="FieldURI", is_attribute=True, is_required=True)
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var field_uri
Inherited members
class ExtendedFieldURI (**kwargs)
-
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/extendedfielduri
Expand source code
class ExtendedFieldURI(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/extendedfielduri""" ELEMENT_NAME = "ExtendedFieldURI" distinguished_property_set_id = CharField(field_uri="DistinguishedPropertySetId", is_attribute=True) property_set_id = CharField(field_uri="PropertySetId", is_attribute=True) property_tag = CharField(field_uri="PropertyTag", is_attribute=True) property_name = CharField(field_uri="PropertyName", is_attribute=True) property_id = CharField(field_uri="PropertyId", is_attribute=True) property_type = CharField(field_uri="PropertyType", is_attribute=True)
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var distinguished_property_set_id
var property_id
var property_name
var property_set_id
var property_tag
var property_type
Inherited members
class FailedMailbox (**kwargs)
-
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/failedmailbox
Expand source code
class FailedMailbox(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/failedmailbox""" ELEMENT_NAME = "FailedMailbox" mailbox = CharField(field_uri="Mailbox") error_code = IntegerField(field_uri="ErrorCode") error_message = CharField(field_uri="ErrorMessage") is_archive = BooleanField(field_uri="IsArchive")
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var error_code
var error_message
var is_archive
var mailbox
Inherited members
class FieldURI (**kwargs)
-
Expand source code
class FieldURI(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/fielduri""" ELEMENT_NAME = "FieldURI" field_uri = CharField(field_uri="FieldURI", is_attribute=True, is_required=True)
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var field_uri
Inherited members
class Fields (*fields)
-
A collection type for the FIELDS class attribute. Works like a list but supports fast lookup by name.
Expand source code
class Fields(list): """A collection type for the FIELDS class attribute. Works like a list but supports fast lookup by name.""" def __init__(self, *fields): super().__init__(fields) self._dict = {} for f in fields: # Check for duplicate field names if f.name in self._dict: raise ValueError(f"Field {f!r} is a duplicate") self._dict[f.name] = f def __getitem__(self, idx_or_slice): # Support fast lookup by name. Make sure slicing returns an instance of this class if isinstance(idx_or_slice, str): return self._dict[idx_or_slice] if isinstance(idx_or_slice, int): return super().__getitem__(idx_or_slice) res = super().__getitem__(idx_or_slice) return self.__class__(*res) def __add__(self, other): # Make sure addition returns an instance of this class res = super().__add__(other) return self.__class__(*res) def __iadd__(self, other): for f in other: self.append(f) return self def __contains__(self, item): return item in self._dict def copy(self): return self.__class__(*self) def index_by_name(self, field_name): for i, f in enumerate(self): if f.name == field_name: return i raise ValueError(f"Unknown field name {field_name!r}") def insert(self, index, field): if field.name in self._dict: raise ValueError(f"Field {field!r} is a duplicate") super().insert(index, field) self._dict[field.name] = field def remove(self, field): super().remove(field) del self._dict[field.name] def append(self, field): super().append(field) self._dict[field.name] = field
Ancestors
- builtins.list
Methods
def append(self, field)
-
Append object to the end of the list.
Expand source code
def append(self, field): super().append(field) self._dict[field.name] = field
def copy(self)
-
Return a shallow copy of the list.
Expand source code
def copy(self): return self.__class__(*self)
def index_by_name(self, field_name)
-
Expand source code
def index_by_name(self, field_name): for i, f in enumerate(self): if f.name == field_name: return i raise ValueError(f"Unknown field name {field_name!r}")
def insert(self, index, field)
-
Insert object before index.
Expand source code
def insert(self, index, field): if field.name in self._dict: raise ValueError(f"Field {field!r} is a duplicate") super().insert(index, field) self._dict[field.name] = field
def remove(self, field)
-
Remove first occurrence of value.
Raises ValueError if the value is not present.
Expand source code
def remove(self, field): super().remove(field) del self._dict[field.name]
class FolderId (*args, **kwargs)
-
Expand source code
class FolderId(ItemId): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/folderid""" ELEMENT_NAME = "FolderId"
Ancestors
Subclasses
Class variables
var ELEMENT_NAME
Inherited members
class FreeBusyChangedEvent (**kwargs)
-
Expand source code
class FreeBusyChangedEvent(TimestampEvent): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/freebusychangedevent""" ELEMENT_NAME = "FreeBusyChangedEvent"
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class FreeBusyView (**kwargs)
-
Expand source code
class FreeBusyView(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/freebusyview""" ELEMENT_NAME = "FreeBusyView" NAMESPACE = MNS view_type = ChoiceField( field_uri="FreeBusyViewType", choices={ Choice("None"), Choice("MergedOnly"), Choice("FreeBusy"), Choice("FreeBusyMerged"), Choice("Detailed"), Choice("DetailedMerged"), }, is_required=True, ) # A string of digits. Each digit points to a position in .fields.FREE_BUSY_CHOICES merged = CharField(field_uri="MergedFreeBusy") calendar_events = EWSElementListField(field_uri="CalendarEventArray", value_cls=CalendarEvent) # WorkingPeriod is located inside the WorkingPeriodArray element which is inside the WorkingHours element working_hours = EWSElementListField(field_uri="WorkingPeriodArray", value_cls=WorkingPeriod) # TimeZone is also inside the WorkingHours element. It contains information about the timezone which the # account is located in. working_hours_timezone = EWSElementField(value_cls=TimeZone) @classmethod def from_xml(cls, elem, account): kwargs = {} working_hours_elem = elem.find(f"{{{TNS}}}WorkingHours") for f in cls.FIELDS: if f.name in ("working_hours", "working_hours_timezone"): if working_hours_elem is None: continue kwargs[f.name] = f.from_xml(elem=working_hours_elem, account=account) continue kwargs[f.name] = f.from_xml(elem=elem, account=account) cls._clear(elem) return cls(**kwargs)
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
var NAMESPACE
Static methods
def from_xml(elem, account)
-
Expand source code
@classmethod def from_xml(cls, elem, account): kwargs = {} working_hours_elem = elem.find(f"{{{TNS}}}WorkingHours") for f in cls.FIELDS: if f.name in ("working_hours", "working_hours_timezone"): if working_hours_elem is None: continue kwargs[f.name] = f.from_xml(elem=working_hours_elem, account=account) continue kwargs[f.name] = f.from_xml(elem=elem, account=account) cls._clear(elem) return cls(**kwargs)
Instance variables
var calendar_events
var merged
var view_type
var working_hours
var working_hours_timezone
Inherited members
class FreeBusyViewOptions (**kwargs)
-
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/freebusyviewoptions
Expand source code
class FreeBusyViewOptions(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/freebusyviewoptions""" ELEMENT_NAME = "FreeBusyViewOptions" REQUESTED_VIEWS = {"MergedOnly", "FreeBusy", "FreeBusyMerged", "Detailed", "DetailedMerged"} time_window = EWSElementField(value_cls=TimeWindow, is_required=True) # Interval value is in minutes merged_free_busy_interval = IntegerField( field_uri="MergedFreeBusyIntervalInMinutes", min=5, max=1440, default=30, is_required=True ) requested_view = ChoiceField( field_uri="RequestedView", choices={Choice(c) for c in REQUESTED_VIEWS}, is_required=True ) # Choice('None') is also valid, but only for responses
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
var REQUESTED_VIEWS
Instance variables
var merged_free_busy_interval
var requested_view
var time_window
Inherited members
class HTMLBody (...)
-
Helper to mark the 'body' field as a complex attribute.
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/body
Expand source code
class HTMLBody(Body): """Helper to mark the 'body' field as a complex attribute. MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/body """ body_type = "HTML"
Ancestors
- Body
- builtins.str
Class variables
var body_type
Inherited members
class IdChangeKeyMixIn (**kwargs)
-
Base class for classes that have a concept of 'id' and 'changekey' values. The values are actually stored on a separate element but we add convenience methods to hide that fact.
Expand source code
class IdChangeKeyMixIn(EWSElement, metaclass=EWSMeta): """Base class for classes that have a concept of 'id' and 'changekey' values. The values are actually stored on a separate element but we add convenience methods to hide that fact. """ ID_ELEMENT_CLS = None def __init__(self, **kwargs): _id, _changekey = kwargs.pop("id", None), kwargs.pop("changekey", None) if _id or _changekey: kwargs["_id"] = self.ID_ELEMENT_CLS(_id, _changekey) super().__init__(**kwargs) @classmethod def get_field_by_fieldname(cls, fieldname): if fieldname in ("id", "changekey"): return cls.ID_ELEMENT_CLS.get_field_by_fieldname(fieldname=fieldname) return super().get_field_by_fieldname(fieldname=fieldname) @property def id(self): if self._id is None: return None return self._id.id @id.setter def id(self, value): if self._id is None: self._id = self.ID_ELEMENT_CLS() self._id.id = value @property def changekey(self): if self._id is None: return None return self._id.changekey @changekey.setter def changekey(self, value): if self._id is None: self._id = self.ID_ELEMENT_CLS() self._id.changekey = value @classmethod def id_from_xml(cls, elem): # This method must be reasonably fast id_elem = elem.find(cls.ID_ELEMENT_CLS.response_tag()) if id_elem is None: return None, None return id_elem.get(cls.ID_ELEMENT_CLS.ID_ATTR), id_elem.get(cls.ID_ELEMENT_CLS.CHANGEKEY_ATTR) def to_id(self): if self._id is None: raise ValueError("Must have an ID") return self._id def __eq__(self, other): if isinstance(other, tuple): return hash((self.id, self.changekey)) == hash(other) return super().__eq__(other) def __hash__(self): # If we have an ID and changekey, use that as key. Else return a hash of all attributes if self.id: return hash((self.id, self.changekey)) return super().__hash__()
Ancestors
Subclasses
Class variables
var ID_ELEMENT_CLS
Static methods
def get_field_by_fieldname(fieldname)
-
Expand source code
@classmethod def get_field_by_fieldname(cls, fieldname): if fieldname in ("id", "changekey"): return cls.ID_ELEMENT_CLS.get_field_by_fieldname(fieldname=fieldname) return super().get_field_by_fieldname(fieldname=fieldname)
def id_from_xml(elem)
-
Expand source code
@classmethod def id_from_xml(cls, elem): # This method must be reasonably fast id_elem = elem.find(cls.ID_ELEMENT_CLS.response_tag()) if id_elem is None: return None, None return id_elem.get(cls.ID_ELEMENT_CLS.ID_ATTR), id_elem.get(cls.ID_ELEMENT_CLS.CHANGEKEY_ATTR)
Instance variables
var changekey
-
Expand source code
@property def changekey(self): if self._id is None: return None return self._id.changekey
var id
-
Expand source code
@property def id(self): if self._id is None: return None return self._id.id
Methods
def to_id(self)
-
Expand source code
def to_id(self): if self._id is None: raise ValueError("Must have an ID") return self._id
Inherited members
class IndexedFieldURI (**kwargs)
-
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/indexedfielduri
Expand source code
class IndexedFieldURI(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/indexedfielduri""" ELEMENT_NAME = "IndexedFieldURI" field_uri = CharField(field_uri="FieldURI", is_attribute=True, is_required=True) field_index = CharField(field_uri="FieldIndex", is_attribute=True, is_required=True)
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var field_index
var field_uri
Inherited members
class ItemId (*args, **kwargs)
-
'id' and 'changekey' are UUIDs generated by Exchange.
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/itemid
Expand source code
class ItemId(BaseItemId): """'id' and 'changekey' are UUIDs generated by Exchange. MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/itemid """ ELEMENT_NAME = "ItemId" ID_ATTR = "Id" CHANGEKEY_ATTR = "ChangeKey" id = IdField(field_uri=ID_ATTR, is_required=True) changekey = IdField(field_uri=CHANGEKEY_ATTR, is_required=False)
Ancestors
Subclasses
- AssociatedCalendarItemId
- ConversationId
- FolderId
- MovedItemId
- OldItemId
- ParentFolderId
- ParentItemId
- PersonaId
- ReferenceItemId
- SourceId
Class variables
var CHANGEKEY_ATTR
var ELEMENT_NAME
var FIELDS
var ID_ATTR
Instance variables
var changekey
var id
Inherited members
class MailTips (**kwargs)
-
Expand source code
class MailTips(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/mailtips""" ELEMENT_NAME = "MailTips" NAMESPACE = MNS recipient_address = RecipientAddressField() pending_mail_tips = ChoiceField(field_uri="PendingMailTips", choices={Choice(c) for c in MAIL_TIPS_TYPES}) out_of_office = EWSElementField(value_cls=OutOfOffice) mailbox_full = BooleanField(field_uri="MailboxFull") custom_mail_tip = TextField(field_uri="CustomMailTip") total_member_count = IntegerField(field_uri="TotalMemberCount") external_member_count = IntegerField(field_uri="ExternalMemberCount") max_message_size = IntegerField(field_uri="MaxMessageSize") delivery_restricted = BooleanField(field_uri="DeliveryRestricted") is_moderated = BooleanField(field_uri="IsModerated") invalid_recipient = BooleanField(field_uri="InvalidRecipient")
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
var NAMESPACE
Instance variables
var custom_mail_tip
var delivery_restricted
var external_member_count
var invalid_recipient
var is_moderated
var mailbox_full
var max_message_size
var out_of_office
var pending_mail_tips
var recipient_address
var total_member_count
Inherited members
class Mailbox (**kwargs)
-
Expand source code
class Mailbox(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/mailbox""" ELEMENT_NAME = "Mailbox" MAILBOX = "Mailbox" ONE_OFF = "OneOff" MAILBOX_TYPE_CHOICES = { Choice(MAILBOX), Choice("PublicDL"), Choice("PrivateDL"), Choice("Contact"), Choice("PublicFolder"), Choice("Unknown"), Choice(ONE_OFF), Choice("GroupMailbox", supported_from=EXCHANGE_2013), } name = TextField(field_uri="Name") email_address = EmailAddressField(field_uri="EmailAddress") # RoutingType values are not restricted: # https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/routingtype-emailaddresstype routing_type = TextField(field_uri="RoutingType", default="SMTP") mailbox_type = ChoiceField(field_uri="MailboxType", choices=MAILBOX_TYPE_CHOICES, default=MAILBOX) item_id = EWSElementField(value_cls=ItemId, is_read_only=True) def clean(self, version=None): super().clean(version=version) if self.mailbox_type != self.ONE_OFF and not self.email_address and not self.item_id: # A OneOff Mailbox (a one-off member of a personal distribution list) may lack these fields, but other # Mailboxes require at least one. See also "Remarks" section of # https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/mailbox raise ValueError(f"Mailbox type {self.mailbox_type!r} must have either 'email_address' or 'item_id' set") def __hash__(self): # Exchange may add 'mailbox_type' and 'name' on insert. We're satisfied if the item_id or email address matches. if self.item_id: return hash(self.item_id) if self.email_address: return hash(self.email_address.lower()) return super().__hash__()
Ancestors
Subclasses
- Address
- DLMailbox
- EmailAddress
- EmailAddressTypeValue
- PersonaPostalAddressTypeValue
- RecipientAddress
- Room
- RoomList
- SendingAs
Class variables
var ELEMENT_NAME
var FIELDS
var MAILBOX
var MAILBOX_TYPE_CHOICES
var ONE_OFF
Instance variables
var email_address
var item_id
var mailbox_type
var name
var routing_type
Methods
def clean(self, version=None)
-
Expand source code
def clean(self, version=None): super().clean(version=version) if self.mailbox_type != self.ONE_OFF and not self.email_address and not self.item_id: # A OneOff Mailbox (a one-off member of a personal distribution list) may lack these fields, but other # Mailboxes require at least one. See also "Remarks" section of # https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/mailbox raise ValueError(f"Mailbox type {self.mailbox_type!r} must have either 'email_address' or 'item_id' set")
Inherited members
class MailboxData (**kwargs)
-
Expand source code
class MailboxData(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/mailboxdata""" ELEMENT_NAME = "MailboxData" ATTENDEE_TYPES = {"Optional", "Organizer", "Required", "Resource", "Room"} email = EmailField() attendee_type = ChoiceField(field_uri="AttendeeType", choices={Choice(c) for c in ATTENDEE_TYPES}) exclude_conflicts = BooleanField(field_uri="ExcludeConflicts")
Ancestors
Class variables
var ATTENDEE_TYPES
var ELEMENT_NAME
var FIELDS
Instance variables
var attendee_type
var email
var exclude_conflicts
Inherited members
class Member (**kwargs)
-
Expand source code
class Member(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/member-ex15websvcsotherref """ ELEMENT_NAME = "Member" mailbox = MailboxField(is_required=True) status = ChoiceField( field_uri="Status", choices={Choice("Unrecognized"), Choice("Normal"), Choice("Demoted")}, default="Normal" ) def __hash__(self): return hash(self.mailbox)
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var mailbox
var status
Inherited members
class MessageHeader (**kwargs)
-
Expand source code
class MessageHeader(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/internetmessageheader""" ELEMENT_NAME = "InternetMessageHeader" name = TextField(field_uri="HeaderName", is_attribute=True) value = SubField()
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var name
var value
Inherited members
class ModifiedEvent (**kwargs)
-
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/modifiedevent
Expand source code
class ModifiedEvent(TimestampEvent): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/modifiedevent""" ELEMENT_NAME = "ModifiedEvent" unread_count = IntegerField(field_uri="UnreadCount")
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var unread_count
Inherited members
class MovedEvent (**kwargs)
-
Expand source code
class MovedEvent(OldTimestampEvent): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/movedevent""" ELEMENT_NAME = "MovedEvent"
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class MovedItemId (*args, **kwargs)
-
Expand source code
class MovedItemId(ItemId): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/moveditemid""" ELEMENT_NAME = "MovedItemId" NAMESPACE = MNS @classmethod def id_from_xml(cls, elem): item = cls.from_xml(elem=elem, account=None) return item.id, item.changekey
Ancestors
Class variables
var ELEMENT_NAME
var NAMESPACE
Static methods
def id_from_xml(elem)
-
Expand source code
@classmethod def id_from_xml(cls, elem): item = cls.from_xml(elem=elem, account=None) return item.id, item.changekey
Inherited members
class NewMailEvent (**kwargs)
-
Expand source code
class NewMailEvent(TimestampEvent): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/newmailevent""" ELEMENT_NAME = "NewMailEvent"
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class Notification (**kwargs)
-
Expand source code
class Notification(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/notification-ex15websvcsotherref """ ELEMENT_NAME = "Notification" NAMESPACE = MNS subscription_id = CharField(field_uri="SubscriptionId") previous_watermark = CharField(field_uri="PreviousWatermark") more_events = BooleanField(field_uri="MoreEvents") events = GenericEventListField("")
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
var NAMESPACE
Instance variables
var events
var more_events
var previous_watermark
var subscription_id
Inherited members
class OccurrenceItemId (*args, **kwargs)
-
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/occurrenceitemid
Expand source code
class OccurrenceItemId(BaseItemId): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/occurrenceitemid""" ELEMENT_NAME = "OccurrenceItemId" ID_ATTR = "RecurringMasterId" CHANGEKEY_ATTR = "ChangeKey" id = IdField(field_uri=ID_ATTR, is_required=True) changekey = IdField(field_uri=CHANGEKEY_ATTR, is_required=False) instance_index = IntegerField(field_uri="InstanceIndex", is_attribute=True, is_required=True, min=1)
Ancestors
Class variables
var CHANGEKEY_ATTR
var ELEMENT_NAME
var FIELDS
var ID_ATTR
Instance variables
var changekey
var id
var instance_index
Inherited members
class OldFolderId (*args, **kwargs)
-
Expand source code
class OldFolderId(FolderId): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/olditemid""" ELEMENT_NAME = "OldFolderId"
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class OldItemId (*args, **kwargs)
-
Expand source code
class OldItemId(ItemId): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/oldfolderid""" ELEMENT_NAME = "OldItemId"
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class OldParentFolderId (*args, **kwargs)
-
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/oldparentfolderid
Expand source code
class OldParentFolderId(ParentFolderId): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/oldparentfolderid""" ELEMENT_NAME = "OldParentFolderId"
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class OldTimestampEvent (**kwargs)
-
Base class for both item and folder copy/move events.
Expand source code
class OldTimestampEvent(TimestampEvent, metaclass=EWSMeta): """Base class for both item and folder copy/move events.""" old_item_id = EWSElementField(value_cls=OldItemId) old_folder_id = EWSElementField(value_cls=OldFolderId) old_parent_folder_id = EWSElementField(value_cls=OldParentFolderId)
Ancestors
Subclasses
Class variables
var FIELDS
Instance variables
var old_folder_id
var old_item_id
var old_parent_folder_id
Inherited members
class OutOfOffice (**kwargs)
-
Expand source code
class OutOfOffice(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/outofoffice""" ELEMENT_NAME = "OutOfOffice" reply_body = MessageField(field_uri="ReplyBody") start = DateTimeField(field_uri="StartTime", is_required=False) end = DateTimeField(field_uri="EndTime", is_required=False) @classmethod def duration_to_start_end(cls, elem, account): kwargs = {} duration = elem.find(f"{{{TNS}}}Duration") if duration is not None: for attr in ("start", "end"): f = cls.get_field_by_fieldname(attr) kwargs[attr] = f.from_xml(elem=duration, account=account) return kwargs @classmethod def from_xml(cls, elem, account): kwargs = {} for attr in ("reply_body",): f = cls.get_field_by_fieldname(attr) kwargs[attr] = f.from_xml(elem=elem, account=account) kwargs.update(cls.duration_to_start_end(elem=elem, account=account)) cls._clear(elem) return cls(**kwargs)
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Static methods
def duration_to_start_end(elem, account)
-
Expand source code
@classmethod def duration_to_start_end(cls, elem, account): kwargs = {} duration = elem.find(f"{{{TNS}}}Duration") if duration is not None: for attr in ("start", "end"): f = cls.get_field_by_fieldname(attr) kwargs[attr] = f.from_xml(elem=duration, account=account) return kwargs
def from_xml(elem, account)
-
Expand source code
@classmethod def from_xml(cls, elem, account): kwargs = {} for attr in ("reply_body",): f = cls.get_field_by_fieldname(attr) kwargs[attr] = f.from_xml(elem=elem, account=account) kwargs.update(cls.duration_to_start_end(elem=elem, account=account)) cls._clear(elem) return cls(**kwargs)
Instance variables
var end
var reply_body
var start
Inherited members
class ParentFolderId (*args, **kwargs)
-
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/parentfolderid
Expand source code
class ParentFolderId(ItemId): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/parentfolderid""" ELEMENT_NAME = "ParentFolderId"
Ancestors
Subclasses
Class variables
var ELEMENT_NAME
Inherited members
class ParentItemId (*args, **kwargs)
-
Expand source code
class ParentItemId(ItemId): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/parentitemid""" ELEMENT_NAME = "ParentItemId" NAMESPACE = MNS
Ancestors
Class variables
var ELEMENT_NAME
var NAMESPACE
Inherited members
class Period (**kwargs)
-
Expand source code
class Period(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/period""" ELEMENT_NAME = "Period" id = CharField(field_uri="Id", is_attribute=True) name = CharField(field_uri="Name", is_attribute=True) bias = TimeDeltaField(field_uri="Bias", is_attribute=True) @property def bias_in_minutes(self): return int(self.bias.total_seconds()) // 60 # Convert to minutes
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var bias
var bias_in_minutes
-
Expand source code
@property def bias_in_minutes(self): return int(self.bias.total_seconds()) // 60 # Convert to minutes
var id
var name
Inherited members
class Permission (**kwargs)
-
Expand source code
class Permission(BasePermission): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/permission""" ELEMENT_NAME = "Permission" LEVEL_CHOICES = ( "None", "Owner", "PublishingEditor", "Editor", "PublishingAuthor", "Author", "NoneditingAuthor", "Reviewer", "Contributor", "Custom", ) permission_level = ChoiceField( field_uri="CalendarPermissionLevel", choices={Choice(c) for c in LEVEL_CHOICES}, default=LEVEL_CHOICES[0] )
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
var LEVEL_CHOICES
Instance variables
var permission_level
Inherited members
class PermissionSet (**kwargs)
-
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/permissionset-permissionsettype and https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/permissionset-calendarpermissionsettype
Expand source code
class PermissionSet(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/permissionset-permissionsettype and https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/permissionset-calendarpermissionsettype """ # For simplicity, we implement the two distinct but equally names elements as one class. ELEMENT_NAME = "PermissionSet" permissions = EWSElementListField(field_uri="Permissions", value_cls=Permission) calendar_permissions = EWSElementListField(field_uri="CalendarPermissions", value_cls=CalendarPermission) unknown_entries = UnknownEntriesField(field_uri="UnknownEntries")
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var calendar_permissions
var permissions
var unknown_entries
Inherited members
class PersonaId (*args, **kwargs)
-
Expand source code
class PersonaId(ItemId): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/personaid""" ELEMENT_NAME = "PersonaId" NAMESPACE = MNS @classmethod def response_tag(cls): # This element is in MNS in the request and TNS in the response... return f"{{{TNS}}}{cls.ELEMENT_NAME}"
Ancestors
Class variables
var ELEMENT_NAME
var NAMESPACE
Static methods
def response_tag()
-
Expand source code
@classmethod def response_tag(cls): # This element is in MNS in the request and TNS in the response... return f"{{{TNS}}}{cls.ELEMENT_NAME}"
Inherited members
class PersonaPhoneNumberTypeValue (**kwargs)
-
Expand source code
class PersonaPhoneNumberTypeValue(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/value-personaphonenumbertype """ ELEMENT_NAME = "Value" number = CharField(field_uri="Number") type = CharField(field_uri="Type")
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var number
var type
Inherited members
class PersonaPostalAddressTypeValue (**kwargs)
-
Expand source code
class PersonaPostalAddressTypeValue(Mailbox): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/value-personapostaladdresstype """ ELEMENT_NAME = "Value" street = TextField(field_uri="Street") city = TextField(field_uri="City") state = TextField(field_uri="State") country = TextField(field_uri="Country") postal_code = TextField(field_uri="PostalCode") post_office_box = TextField(field_uri="PostOfficeBox") type = TextField(field_uri="Type") latitude = TextField(field_uri="Latitude") longitude = TextField(field_uri="Longitude") accuracy = TextField(field_uri="Accuracy") altitude = TextField(field_uri="Altitude") altitude_accuracy = TextField(field_uri="AltitudeAccuracy") formatted_address = TextField(field_uri="FormattedAddress") location_uri = TextField(field_uri="LocationUri") location_source = TextField(field_uri="LocationSource")
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var accuracy
var altitude
var altitude_accuracy
var city
var country
var formatted_address
var latitude
var location_source
var location_uri
var longitude
var post_office_box
var postal_code
var state
var street
var type
Inherited members
class PhoneNumber (**kwargs)
-
Expand source code
class PhoneNumber(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/phonenumber""" ELEMENT_NAME = "PhoneNumber" number = CharField(field_uri="Number") type = CharField(field_uri="Type")
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var number
var type
Inherited members
class PhoneNumberAttributedValue (**kwargs)
-
Expand source code
class PhoneNumberAttributedValue(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/phonenumberattributedvalue """ ELEMENT_NAME = "PhoneNumberAttributedValue" value = EWSElementField(value_cls=PersonaPhoneNumberTypeValue) attributions = CharListField(field_uri="Attributions", list_elem_name="Attribution")
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var attributions
var value
Inherited members
class PostalAddressAttributedValue (**kwargs)
-
Expand source code
class PostalAddressAttributedValue(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/postaladdressattributedvalue """ ELEMENT_NAME = "PostalAddressAttributedValue" value = EWSElementField(value_cls=PersonaPostalAddressTypeValue) attributions = EWSElementListField(field_uri="Attributions", value_cls=Attribution)
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var attributions
var value
Inherited members
class RecipientAddress (**kwargs)
-
Like Mailbox, but with a different tag name.
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/recipientaddress
Expand source code
class RecipientAddress(Mailbox): """Like Mailbox, but with a different tag name. MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/recipientaddress """ ELEMENT_NAME = "RecipientAddress"
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class RecurringDateTransition (**kwargs)
-
Expand source code
class RecurringDateTransition(BaseTransition): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/recurringdatetransition""" ELEMENT_NAME = "RecurringDateTransition" offset = TimeDeltaField(field_uri="TimeOffset") month = IntegerField(field_uri="Month") day = IntegerField(field_uri="Day") # Day of month
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var day
var month
var offset
Inherited members
class RecurringDayTransition (**kwargs)
-
Expand source code
class RecurringDayTransition(BaseTransition): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/recurringdaytransition""" ELEMENT_NAME = "RecurringDayTransition" offset = TimeDeltaField(field_uri="TimeOffset") month = IntegerField(field_uri="Month") # Valid ISO 8601 weekday, as a number in range 1 -> 7 (1 being Monday) day_of_week = EnumField(field_uri="DayOfWeek", enum=WEEKDAY_NAMES) occurrence = IntegerField(field_uri="Occurrence") @classmethod def from_xml(cls, elem, account): res = super().from_xml(elem, account) # See TimeZoneTransition.from_xml() if res.occurrence == -1: res.occurrence = 5 return res
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Static methods
def from_xml(elem, account)
-
Expand source code
@classmethod def from_xml(cls, elem, account): res = super().from_xml(elem, account) # See TimeZoneTransition.from_xml() if res.occurrence == -1: res.occurrence = 5 return res
Instance variables
var day_of_week
var month
var occurrence
var offset
Inherited members
class RecurringMasterItemId (*args, **kwargs)
-
Expand source code
class RecurringMasterItemId(BaseItemId): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/recurringmasteritemid""" ELEMENT_NAME = "RecurringMasterItemId" ID_ATTR = "OccurrenceId" CHANGEKEY_ATTR = "ChangeKey" id = IdField(field_uri=ID_ATTR, is_required=True) changekey = IdField(field_uri=CHANGEKEY_ATTR, is_required=False)
Ancestors
Class variables
var CHANGEKEY_ATTR
var ELEMENT_NAME
var FIELDS
var ID_ATTR
Instance variables
var changekey
var id
Inherited members
class ReferenceItemId (*args, **kwargs)
-
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/referenceitemid
Expand source code
class ReferenceItemId(ItemId): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/referenceitemid""" ELEMENT_NAME = "ReferenceItemId"
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class ReminderMessageData (**kwargs)
-
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/remindermessagedata
Expand source code
class ReminderMessageData(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/remindermessagedata""" ELEMENT_NAME = "ReminderMessageData" reminder_text = CharField(field_uri="ReminderText") location = CharField(field_uri="Location") start_time = TimeField(field_uri="StartTime") end_time = TimeField(field_uri="EndTime") associated_calendar_item_id = AssociatedCalendarItemIdField( field_uri="AssociatedCalendarItemId", supported_from=Build(15, 0, 913, 9) )
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var associated_calendar_item_id
var end_time
var location
var reminder_text
var start_time
Inherited members
class RemoveItem (**kwargs)
-
Expand source code
class RemoveItem(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/removeitem""" ELEMENT_NAME = "RemoveItem" reference_item_id = ReferenceItemIdField(field_uri="item:ReferenceItemId")
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var reference_item_id
Inherited members
class ResponseObjects (**kwargs)
-
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/responseobjects
Expand source code
class ResponseObjects(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/responseobjects""" ELEMENT_NAME = "ResponseObjects" accept_item = EWSElementField(field_uri="AcceptItem", value_cls="AcceptItem", namespace=TNS) tentatively_accept_item = EWSElementField( field_uri="TentativelyAcceptItem", value_cls="TentativelyAcceptItem", namespace=TNS ) decline_item = EWSElementField(field_uri="DeclineItem", value_cls="DeclineItem", namespace=TNS) reply_to_item = EWSElementField(field_uri="ReplyToItem", value_cls="ReplyToItem", namespace=TNS) forward_item = EWSElementField(field_uri="ForwardItem", value_cls="ForwardItem", namespace=TNS) reply_all_to_item = EWSElementField(field_uri="ReplyAllToItem", value_cls="ReplyAllToItem", namespace=TNS) cancel_calendar_item = EWSElementField( field_uri="CancelCalendarItem", value_cls="CancelCalendarItem", namespace=TNS ) remove_item = EWSElementField(field_uri="RemoveItem", value_cls=RemoveItem) post_reply_item = EWSElementField(field_uri="PostReplyItem", value_cls="PostReplyItem", namespace=TNS) success_read_receipt = EWSElementField(field_uri="SuppressReadReceipt", value_cls=SuppressReadReceipt) accept_sharing_invitation = EWSElementField(field_uri="AcceptSharingInvitation", value_cls=AcceptSharingInvitation)
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var accept_item
var accept_sharing_invitation
var cancel_calendar_item
var decline_item
var forward_item
var post_reply_item
var remove_item
var reply_all_to_item
var reply_to_item
var success_read_receipt
var tentatively_accept_item
Inherited members
class Room (**kwargs)
-
Expand source code
class Room(Mailbox): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/room""" ELEMENT_NAME = "Room" @classmethod def from_xml(cls, elem, account): id_elem = elem.find(f"{{{TNS}}}Id") item_id_elem = id_elem.find(ItemId.response_tag()) kwargs = dict( name=get_xml_attr(id_elem, f"{{{TNS}}}Name"), email_address=get_xml_attr(id_elem, f"{{{TNS}}}EmailAddress"), mailbox_type=get_xml_attr(id_elem, f"{{{TNS}}}MailboxType"), item_id=ItemId.from_xml(elem=item_id_elem, account=account) if item_id_elem else None, ) cls._clear(elem) return cls(**kwargs)
Ancestors
Class variables
var ELEMENT_NAME
Static methods
def from_xml(elem, account)
-
Expand source code
@classmethod def from_xml(cls, elem, account): id_elem = elem.find(f"{{{TNS}}}Id") item_id_elem = id_elem.find(ItemId.response_tag()) kwargs = dict( name=get_xml_attr(id_elem, f"{{{TNS}}}Name"), email_address=get_xml_attr(id_elem, f"{{{TNS}}}EmailAddress"), mailbox_type=get_xml_attr(id_elem, f"{{{TNS}}}MailboxType"), item_id=ItemId.from_xml(elem=item_id_elem, account=account) if item_id_elem else None, ) cls._clear(elem) return cls(**kwargs)
Inherited members
class RoomList (**kwargs)
-
Expand source code
class RoomList(Mailbox): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/roomlist""" ELEMENT_NAME = "RoomList" NAMESPACE = MNS @classmethod def response_tag(cls): # In a GetRoomLists response, room lists are delivered as Address elements. See # https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/address-emailaddresstype return f"{{{TNS}}}Address"
Ancestors
Class variables
var ELEMENT_NAME
var NAMESPACE
Static methods
def response_tag()
-
Expand source code
@classmethod def response_tag(cls): # In a GetRoomLists response, room lists are delivered as Address elements. See # https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/address-emailaddresstype return f"{{{TNS}}}Address"
Inherited members
class RootItemId (*args, **kwargs)
-
Expand source code
class RootItemId(BaseItemId): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/rootitemid""" ELEMENT_NAME = "RootItemId" NAMESPACE = MNS ID_ATTR = "RootItemId" CHANGEKEY_ATTR = "RootItemChangeKey" id = IdField(field_uri=ID_ATTR, is_required=True) changekey = IdField(field_uri=CHANGEKEY_ATTR, is_required=True)
Ancestors
Class variables
var CHANGEKEY_ATTR
var ELEMENT_NAME
var FIELDS
var ID_ATTR
var NAMESPACE
Instance variables
var changekey
var id
Inherited members
class SearchableMailbox (**kwargs)
-
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/searchablemailbox
Expand source code
class SearchableMailbox(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/searchablemailbox""" ELEMENT_NAME = "SearchableMailbox" guid = CharField(field_uri="Guid") primary_smtp_address = EmailAddressField(field_uri="PrimarySmtpAddress") is_external = BooleanField(field_uri="IsExternalMailbox") external_email = EmailAddressField(field_uri="ExternalEmailAddress") display_name = CharField(field_uri="DisplayName") is_membership_group = BooleanField(field_uri="IsMembershipGroup") reference_id = CharField(field_uri="ReferenceId")
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var display_name
var external_email
var guid
var is_external
var is_membership_group
var primary_smtp_address
var reference_id
Inherited members
class SendingAs (**kwargs)
-
Like Mailbox, but creates elements in the 'messages' namespace when sending requests.
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/sendingas
Expand source code
class SendingAs(Mailbox): """Like Mailbox, but creates elements in the 'messages' namespace when sending requests. MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/sendingas """ ELEMENT_NAME = "SendingAs" NAMESPACE = MNS
Ancestors
Class variables
var ELEMENT_NAME
var NAMESPACE
Inherited members
class SourceId (*args, **kwargs)
-
Expand source code
class SourceId(ItemId): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/sourceid""" ELEMENT_NAME = "SourceId"
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class StandardTime (**kwargs)
-
Expand source code
class StandardTime(TimeZoneTransition): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/standardtime""" ELEMENT_NAME = "StandardTime"
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class StatusEvent (**kwargs)
-
Expand source code
class StatusEvent(Event): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/statusevent""" ELEMENT_NAME = "StatusEvent"
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class StringAttributedValue (**kwargs)
-
Expand source code
class StringAttributedValue(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/stringattributedvalue """ ELEMENT_NAME = "StringAttributedValue" value = CharField(field_uri="Value") attributions = CharListField(field_uri="Attributions", list_elem_name="Attribution")
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var attributions
var value
Inherited members
class SuppressReadReceipt (**kwargs)
-
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/suppressreadreceipt
Expand source code
class SuppressReadReceipt(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/suppressreadreceipt""" ELEMENT_NAME = "SuppressReadReceipt" reference_item_id = ReferenceItemIdField(field_uri="item:ReferenceItemId")
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var reference_item_id
Inherited members
class TimeWindow (**kwargs)
-
Expand source code
class TimeWindow(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/timewindow""" ELEMENT_NAME = "TimeWindow" start = DateTimeField(field_uri="StartTime", is_required=True) end = DateTimeField(field_uri="EndTime", is_required=True) def clean(self, version=None): if self.start >= self.end: raise ValueError(f"'start' must be less than 'end' ({self.start} -> {self.end})") super().clean(version=version)
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var end
var start
Methods
def clean(self, version=None)
-
Expand source code
def clean(self, version=None): if self.start >= self.end: raise ValueError(f"'start' must be less than 'end' ({self.start} -> {self.end})") super().clean(version=version)
Inherited members
class TimeZone (**kwargs)
-
Expand source code
class TimeZone(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/timezone-availability""" ELEMENT_NAME = "TimeZone" bias = IntegerField(field_uri="Bias", is_required=True) # Standard (non-DST) offset from UTC, in minutes standard_time = EWSElementField(value_cls=StandardTime) daylight_time = EWSElementField(value_cls=DaylightTime) def to_server_timezone(self, timezones, for_year): """Return the Microsoft timezone ID corresponding to this timezone. There may not be a match at all, and there may be multiple matches. If so, return a random timezone ID. :param timezones: A list of server timezones, as returned by Protocol.get_timezones(return_full_timezone_data=True) :param for_year: return: A Microsoft timezone ID, as a string :return: A Microsoft timezone ID, as a string """ candidates = set() for tz_definition in timezones: candidate = self.from_server_timezone( tz_definition=tz_definition, for_year=for_year, ) if candidate == self: log.debug("Found exact candidate: %s (%s)", tz_definition.id, tz_definition.name) # We prefer this timezone over anything else. Return immediately. return tz_definition.id # Reduce list based on base bias and standard / daylight bias values if candidate.bias != self.bias: continue if candidate.standard_time is None: if self.standard_time is not None: continue else: if self.standard_time is None: continue if candidate.standard_time.bias != self.standard_time.bias: continue if candidate.daylight_time is None: if self.daylight_time is not None: continue else: if self.daylight_time is None: continue if candidate.daylight_time.bias != self.daylight_time.bias: continue log.debug("Found candidate with matching biases: %s (%s)", tz_definition.id, tz_definition.name) candidates.add(tz_definition.id) if not candidates: raise ValueError("No server timezones match this timezone definition") if len(candidates) == 1: log.info("Could not find an exact timezone match for %s. Selecting the best candidate", self) else: log.warning("Could not find an exact timezone match for %s. Selecting a random candidate", self) return candidates.pop() @classmethod def from_server_timezone(cls, tz_definition, for_year): # Creates a TimeZone object from the result of a GetServerTimeZones call with full timezone data std_time, daylight_time, period = tz_definition.get_std_and_dst(for_year=for_year) return cls(bias=period.bias_in_minutes, standard_time=std_time, daylight_time=daylight_time)
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Static methods
def from_server_timezone(tz_definition, for_year)
-
Expand source code
@classmethod def from_server_timezone(cls, tz_definition, for_year): # Creates a TimeZone object from the result of a GetServerTimeZones call with full timezone data std_time, daylight_time, period = tz_definition.get_std_and_dst(for_year=for_year) return cls(bias=period.bias_in_minutes, standard_time=std_time, daylight_time=daylight_time)
Instance variables
var bias
var daylight_time
var standard_time
Methods
def to_server_timezone(self, timezones, for_year)
-
Return the Microsoft timezone ID corresponding to this timezone. There may not be a match at all, and there may be multiple matches. If so, return a random timezone ID.
:param timezones: A list of server timezones, as returned by Protocol.get_timezones(return_full_timezone_data=True) :param for_year: return: A Microsoft timezone ID, as a string
:return: A Microsoft timezone ID, as a string
Expand source code
def to_server_timezone(self, timezones, for_year): """Return the Microsoft timezone ID corresponding to this timezone. There may not be a match at all, and there may be multiple matches. If so, return a random timezone ID. :param timezones: A list of server timezones, as returned by Protocol.get_timezones(return_full_timezone_data=True) :param for_year: return: A Microsoft timezone ID, as a string :return: A Microsoft timezone ID, as a string """ candidates = set() for tz_definition in timezones: candidate = self.from_server_timezone( tz_definition=tz_definition, for_year=for_year, ) if candidate == self: log.debug("Found exact candidate: %s (%s)", tz_definition.id, tz_definition.name) # We prefer this timezone over anything else. Return immediately. return tz_definition.id # Reduce list based on base bias and standard / daylight bias values if candidate.bias != self.bias: continue if candidate.standard_time is None: if self.standard_time is not None: continue else: if self.standard_time is None: continue if candidate.standard_time.bias != self.standard_time.bias: continue if candidate.daylight_time is None: if self.daylight_time is not None: continue else: if self.daylight_time is None: continue if candidate.daylight_time.bias != self.daylight_time.bias: continue log.debug("Found candidate with matching biases: %s (%s)", tz_definition.id, tz_definition.name) candidates.add(tz_definition.id) if not candidates: raise ValueError("No server timezones match this timezone definition") if len(candidates) == 1: log.info("Could not find an exact timezone match for %s. Selecting the best candidate", self) else: log.warning("Could not find an exact timezone match for %s. Selecting a random candidate", self) return candidates.pop()
Inherited members
class TimeZoneDefinition (**kwargs)
-
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/timezonedefinition
Expand source code
class TimeZoneDefinition(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/timezonedefinition""" ELEMENT_NAME = "TimeZoneDefinition" id = CharField(field_uri="Id", is_attribute=True) name = CharField(field_uri="Name", is_attribute=True) periods = EWSElementListField(field_uri="Periods", value_cls=Period) transitions_groups = EWSElementListField(field_uri="TransitionsGroups", value_cls=TransitionsGroup) transitions = TransitionListField(field_uri="Transitions", value_cls=BaseTransition) @classmethod def from_xml(cls, elem, account): return super().from_xml(elem, account) def _get_standard_period(self, transitions_group): # Find the first standard period referenced from transitions_group standard_periods_map = {p.id: p for p in self.periods if p.name == "Standard"} for transition in transitions_group.transitions: try: return standard_periods_map[transition.to] except KeyError: continue raise ValueError(f"No standard period matching any transition in {transitions_group}") def _get_transitions_group(self, for_year): # Look through the transitions, and pick the relevant transition group according to the 'for_year' value transitions_group = None transitions_groups_map = {tg.id: tg for tg in self.transitions_groups} for transition in sorted(self.transitions, key=lambda t: t.to): if transition.kind != "Group": continue if isinstance(transition, AbsoluteDateTransition) and transition.date.year > for_year: break transitions_group = transitions_groups_map[transition.to] if transitions_group is None: raise ValueError(f"No valid transition group for year {for_year}: {self.transitions}") return transitions_group def get_std_and_dst(self, for_year): # Return 'standard_time' and 'daylight_time' objects. We do unnecessary work here, but it keeps code simple. transitions_group = self._get_transitions_group(for_year) if not 0 <= len(transitions_group.transitions) <= 2: raise ValueError(f"Expected 0-2 transitions in transitions group {transitions_group}") standard_period = self._get_standard_period(transitions_group) periods_map = {p.id: p for p in self.periods} standard_time, daylight_time = None, None if len(transitions_group.transitions) == 1: # This is a simple transition group representing a timezone with no DST. Some servers don't accept # TimeZone elements without a STD and DST element (see issue #488). Return StandardTime and DaylightTime # objects with dummy values and 0 bias - this satisfies the broken servers and hopefully doesn't break # the well-behaving servers. standard_time = StandardTime(bias=0, time=datetime.time(0), occurrence=1, iso_month=1, weekday=1) daylight_time = DaylightTime(bias=0, time=datetime.time(0), occurrence=5, iso_month=12, weekday=7) return standard_time, daylight_time, standard_period for transition in transitions_group.transitions: # 'offset' is the time of day to transition, as timedelta since midnight. Check that it's a reasonable value transition.clean(version=None) transition_kwargs = dict( time=(datetime.datetime(2000, 1, 1) + transition.offset).time(), occurrence=transition.occurrence, iso_month=transition.month, weekday=transition.day_of_week, ) period = periods_map[transition.to] if period.name == "Standard": transition_kwargs["bias"] = 0 standard_time = StandardTime(**transition_kwargs) continue if period.name == "Daylight": transition_kwargs["bias"] = period.bias_in_minutes - standard_period.bias_in_minutes daylight_time = DaylightTime(**transition_kwargs) continue raise ValueError(f"Unknown transition: {transition}") return standard_time, daylight_time, standard_period
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Static methods
def from_xml(elem, account)
-
Expand source code
@classmethod def from_xml(cls, elem, account): return super().from_xml(elem, account)
Instance variables
var id
var name
var periods
var transitions
var transitions_groups
Methods
def get_std_and_dst(self, for_year)
-
Expand source code
def get_std_and_dst(self, for_year): # Return 'standard_time' and 'daylight_time' objects. We do unnecessary work here, but it keeps code simple. transitions_group = self._get_transitions_group(for_year) if not 0 <= len(transitions_group.transitions) <= 2: raise ValueError(f"Expected 0-2 transitions in transitions group {transitions_group}") standard_period = self._get_standard_period(transitions_group) periods_map = {p.id: p for p in self.periods} standard_time, daylight_time = None, None if len(transitions_group.transitions) == 1: # This is a simple transition group representing a timezone with no DST. Some servers don't accept # TimeZone elements without a STD and DST element (see issue #488). Return StandardTime and DaylightTime # objects with dummy values and 0 bias - this satisfies the broken servers and hopefully doesn't break # the well-behaving servers. standard_time = StandardTime(bias=0, time=datetime.time(0), occurrence=1, iso_month=1, weekday=1) daylight_time = DaylightTime(bias=0, time=datetime.time(0), occurrence=5, iso_month=12, weekday=7) return standard_time, daylight_time, standard_period for transition in transitions_group.transitions: # 'offset' is the time of day to transition, as timedelta since midnight. Check that it's a reasonable value transition.clean(version=None) transition_kwargs = dict( time=(datetime.datetime(2000, 1, 1) + transition.offset).time(), occurrence=transition.occurrence, iso_month=transition.month, weekday=transition.day_of_week, ) period = periods_map[transition.to] if period.name == "Standard": transition_kwargs["bias"] = 0 standard_time = StandardTime(**transition_kwargs) continue if period.name == "Daylight": transition_kwargs["bias"] = period.bias_in_minutes - standard_period.bias_in_minutes daylight_time = DaylightTime(**transition_kwargs) continue raise ValueError(f"Unknown transition: {transition}") return standard_time, daylight_time, standard_period
Inherited members
class TimeZoneTransition (**kwargs)
-
Base class for StandardTime and DaylightTime classes.
Expand source code
class TimeZoneTransition(EWSElement, metaclass=EWSMeta): """Base class for StandardTime and DaylightTime classes.""" bias = IntegerField(field_uri="Bias", is_required=True) # Offset from the default bias, in minutes time = TimeField(field_uri="Time", is_required=True) occurrence = IntegerField(field_uri="DayOrder", is_required=True) # n'th occurrence of weekday in iso_month iso_month = IntegerField(field_uri="Month", is_required=True) weekday = EnumField(field_uri="DayOfWeek", enum=WEEKDAY_NAMES, is_required=True) # 'Year' is not implemented yet @classmethod def from_xml(cls, elem, account): res = super().from_xml(elem, account) # Some parts of EWS use '5' to mean 'last occurrence in month', others use '-1'. Let's settle on '5' because # only '5' is accepted in requests. if res.occurrence == -1: res.occurrence = 5 return res def clean(self, version=None): super().clean(version=version) if self.occurrence == -1: # See from_xml() self.occurrence = 5
Ancestors
Subclasses
Class variables
var FIELDS
Static methods
def from_xml(elem, account)
-
Expand source code
@classmethod def from_xml(cls, elem, account): res = super().from_xml(elem, account) # Some parts of EWS use '5' to mean 'last occurrence in month', others use '-1'. Let's settle on '5' because # only '5' is accepted in requests. if res.occurrence == -1: res.occurrence = 5 return res
Instance variables
var bias
var iso_month
var occurrence
var time
var weekday
Methods
def clean(self, version=None)
-
Expand source code
def clean(self, version=None): super().clean(version=version) if self.occurrence == -1: # See from_xml() self.occurrence = 5
Inherited members
class TimestampEvent (**kwargs)
-
Base class for both item and folder events with a timestamp.
Expand source code
class TimestampEvent(Event, metaclass=EWSMeta): """Base class for both item and folder events with a timestamp.""" FOLDER = "folder" ITEM = "item" timestamp = DateTimeField(field_uri="TimeStamp") item_id = EWSElementField(value_cls=ItemId) folder_id = EWSElementField(value_cls=FolderId) parent_folder_id = EWSElementField(value_cls=ParentFolderId) @property def event_type(self): if self.item_id is not None: return self.ITEM if self.folder_id is not None: return self.FOLDER return None # Empty object
Ancestors
Subclasses
Class variables
var FIELDS
var FOLDER
var ITEM
Instance variables
var event_type
-
Expand source code
@property def event_type(self): if self.item_id is not None: return self.ITEM if self.folder_id is not None: return self.FOLDER return None # Empty object
var folder_id
var item_id
var parent_folder_id
var timestamp
Inherited members
class Transition (**kwargs)
-
Expand source code
class Transition(BaseTransition): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/transition""" ELEMENT_NAME = "Transition"
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class TransitionsGroup (**kwargs)
-
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/transitionsgroup
Expand source code
class TransitionsGroup(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/transitionsgroup""" ELEMENT_NAME = "TransitionsGroup" id = CharField(field_uri="Id", is_attribute=True) transitions = TransitionListField(value_cls=BaseTransition)
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var id
var transitions
Inherited members
class UID (uid)
-
Helper class to encode Calendar UIDs. See issue #453. Example:
class GlobalObjectId(ExtendedProperty): distinguished_property_set_id = 'Meeting' property_id = 3 property_type = 'Binary'
CalendarItem.register('global_object_id', GlobalObjectId) account.calendar.filter(global_object_id=UID('261cbc18-1f65-5a0a-bd11-23b1e224cc2f'))
Expand source code
class UID(bytes): """Helper class to encode Calendar UIDs. See issue #453. Example: class GlobalObjectId(ExtendedProperty): distinguished_property_set_id = 'Meeting' property_id = 3 property_type = 'Binary' CalendarItem.register('global_object_id', GlobalObjectId) account.calendar.filter(global_object_id=UID('261cbc18-1f65-5a0a-bd11-23b1e224cc2f')) """ _HEADER = binascii.hexlify( bytearray((0x04, 0x00, 0x00, 0x00, 0x82, 0x00, 0xE0, 0x00, 0x74, 0xC5, 0xB7, 0x10, 0x1A, 0x82, 0xE0, 0x08)) ) _EXCEPTION_REPLACEMENT_TIME = binascii.hexlify(bytearray((0, 0, 0, 0))) _CREATION_TIME = binascii.hexlify(bytearray((0, 0, 0, 0, 0, 0, 0, 0))) _RESERVED = binascii.hexlify(bytearray((0, 0, 0, 0, 0, 0, 0, 0))) # https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxocal/1d3aac05-a7b9-45cc-a213-47f0a0a2c5c1 # https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-asemail/e7424ddc-dd10-431e-a0b7-5c794863370e # https://stackoverflow.com/questions/42259122 # https://stackoverflow.com/questions/33757805 def __new__(cls, uid): payload = binascii.hexlify(bytearray(f"vCal-Uid\x01\x00\x00\x00{uid}\x00".encode("ascii"))) length = binascii.hexlify(bytearray(struct.pack("<I", int(len(payload) / 2)))) encoding = b"".join( [cls._HEADER, cls._EXCEPTION_REPLACEMENT_TIME, cls._CREATION_TIME, cls._RESERVED, length, payload] ) return super().__new__(cls, codecs.decode(encoding, "hex")) @classmethod def to_global_object_id(cls, uid): """Converts a UID as returned by EWS to GlobalObjectId format""" return binascii.unhexlify(uid)
Ancestors
- builtins.bytes
Static methods
def to_global_object_id(uid)
-
Converts a UID as returned by EWS to GlobalObjectId format
Expand source code
@classmethod def to_global_object_id(cls, uid): """Converts a UID as returned by EWS to GlobalObjectId format""" return binascii.unhexlify(uid)
class UserConfiguration (**kwargs)
-
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/userconfiguration
Expand source code
class UserConfiguration(IdChangeKeyMixIn): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/userconfiguration""" ELEMENT_NAME = "UserConfiguration" NAMESPACE = MNS ID_ELEMENT_CLS = ItemId _id = IdElementField(field_uri="ItemId", value_cls=ID_ELEMENT_CLS) user_configuration_name = EWSElementField(value_cls=UserConfigurationName) dictionary = DictionaryField(field_uri="Dictionary") xml_data = Base64Field(field_uri="XmlData") binary_data = Base64Field(field_uri="BinaryData")
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
var ID_ELEMENT_CLS
-
'id' and 'changekey' are UUIDs generated by Exchange.
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/itemid
var NAMESPACE
Instance variables
var binary_data
var dictionary
var user_configuration_name
var xml_data
Inherited members
class UserConfigurationName (**kwargs)
-
Expand source code
class UserConfigurationName(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/userconfigurationname""" ELEMENT_NAME = "UserConfigurationName" NAMESPACE = TNS name = CharField(field_uri="Name", is_attribute=True) folder = EWSElementField(value_cls=FolderId) def clean(self, version=None): from .folders import BaseFolder if isinstance(self.folder, BaseFolder): self.folder = self.folder.to_id() super().clean(version=version) @classmethod def from_xml(cls, elem, account): # We also accept distinguished folders f = EWSElementField(value_cls=DistinguishedFolderId) distinguished_folder_id = f.from_xml(elem=elem, account=account) res = super().from_xml(elem=elem, account=account) if distinguished_folder_id: res.folder = distinguished_folder_id return res
Ancestors
Subclasses
Class variables
var ELEMENT_NAME
var FIELDS
var NAMESPACE
Static methods
def from_xml(elem, account)
-
Expand source code
@classmethod def from_xml(cls, elem, account): # We also accept distinguished folders f = EWSElementField(value_cls=DistinguishedFolderId) distinguished_folder_id = f.from_xml(elem=elem, account=account) res = super().from_xml(elem=elem, account=account) if distinguished_folder_id: res.folder = distinguished_folder_id return res
Instance variables
var folder
var name
Methods
def clean(self, version=None)
-
Expand source code
def clean(self, version=None): from .folders import BaseFolder if isinstance(self.folder, BaseFolder): self.folder = self.folder.to_id() super().clean(version=version)
Inherited members
class UserConfigurationNameMNS (**kwargs)
-
Like UserConfigurationName, but in the MNS namespace.
Expand source code
class UserConfigurationNameMNS(UserConfigurationName): """Like UserConfigurationName, but in the MNS namespace. MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/userconfigurationname """ NAMESPACE = MNS
Ancestors
Class variables
var NAMESPACE
Inherited members
class UserId (**kwargs)
-
Expand source code
class UserId(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/userid""" ELEMENT_NAME = "UserId" sid = CharField(field_uri="SID") primary_smtp_address = EmailAddressField(field_uri="PrimarySmtpAddress") display_name = CharField(field_uri="DisplayName") distinguished_user = ChoiceField(field_uri="DistinguishedUser", choices={Choice("Default"), Choice("Anonymous")}) external_user_identity = CharField(field_uri="ExternalUserIdentity")
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var display_name
var distinguished_user
var external_user_identity
var primary_smtp_address
var sid
Inherited members
class WorkingPeriod (**kwargs)
-
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/workingperiod
Expand source code
class WorkingPeriod(EWSElement): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/workingperiod""" ELEMENT_NAME = "WorkingPeriod" weekdays = EnumListField(field_uri="DayOfWeek", enum=WEEKDAY_NAMES, is_required=True) start = TimeField(field_uri="StartTimeInMinutes", is_required=True) end = TimeField(field_uri="EndTimeInMinutes", is_required=True)
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var end
var start
var weekdays
Inherited members