Module exchangelib.items
Expand source code
from .base import (
    AFFECTED_TASK_OCCURRENCES_CHOICES,
    ALL_OCCURRENCES,
    ALL_PROPERTIES,
    ALWAYS_OVERWRITE,
    AUTO_RESOLVE,
    CONFLICT_RESOLUTION_CHOICES,
    DEFAULT,
    DELETE_TYPE_CHOICES,
    HARD_DELETE,
    ID_ONLY,
    MESSAGE_DISPOSITION_CHOICES,
    MOVE_TO_DELETED_ITEMS,
    NEVER_OVERWRITE,
    SAVE_ONLY,
    SEND_AND_SAVE_COPY,
    SEND_MEETING_CANCELLATIONS_CHOICES,
    SEND_MEETING_INVITATIONS_AND_CANCELLATIONS_CHOICES,
    SEND_MEETING_INVITATIONS_CHOICES,
    SEND_ONLY,
    SEND_ONLY_TO_ALL,
    SEND_ONLY_TO_CHANGED,
    SEND_TO_ALL_AND_SAVE_COPY,
    SEND_TO_CHANGED_AND_SAVE_COPY,
    SEND_TO_NONE,
    SHAPE_CHOICES,
    SOFT_DELETE,
    SPECIFIED_OCCURRENCE_ONLY,
    BulkCreateResult,
    RegisterMixIn,
)
from .calendar_item import (
    CONFERENCE_TYPES,
    AcceptItem,
    CalendarItem,
    CancelCalendarItem,
    DeclineItem,
    MeetingCancellation,
    MeetingMessage,
    MeetingRequest,
    MeetingResponse,
    TentativelyAcceptItem,
)
from .contact import Contact, DistributionList, Persona
from .item import BaseItem, Item
from .message import ForwardItem, Message, ReplyAllToItem, ReplyToItem
from .post import PostItem, PostReplyItem
from .task import Task
# Traversal enums
SHALLOW = "Shallow"
SOFT_DELETED = "SoftDeleted"
ASSOCIATED = "Associated"
ITEM_TRAVERSAL_CHOICES = (SHALLOW, SOFT_DELETED, ASSOCIATED)
# Contacts search (ResolveNames) scope enums
ACTIVE_DIRECTORY = "ActiveDirectory"
ACTIVE_DIRECTORY_CONTACTS = "ActiveDirectoryContacts"
CONTACTS = "Contacts"
CONTACTS_ACTIVE_DIRECTORY = "ContactsActiveDirectory"
SEARCH_SCOPE_CHOICES = (ACTIVE_DIRECTORY, ACTIVE_DIRECTORY_CONTACTS, CONTACTS, CONTACTS_ACTIVE_DIRECTORY)
ITEM_CLASSES = (
    CalendarItem,
    Contact,
    DistributionList,
    Item,
    Message,
    MeetingMessage,
    MeetingRequest,
    MeetingResponse,
    MeetingCancellation,
    PostItem,
    Task,
)
__all__ = [
    "RegisterMixIn",
    "MESSAGE_DISPOSITION_CHOICES",
    "SAVE_ONLY",
    "SEND_ONLY",
    "SEND_AND_SAVE_COPY",
    "CalendarItem",
    "AcceptItem",
    "TentativelyAcceptItem",
    "DeclineItem",
    "CancelCalendarItem",
    "MeetingRequest",
    "MeetingResponse",
    "MeetingCancellation",
    "CONFERENCE_TYPES",
    "Contact",
    "Persona",
    "DistributionList",
    "SEND_MEETING_INVITATIONS_CHOICES",
    "SEND_TO_NONE",
    "SEND_ONLY_TO_ALL",
    "SEND_TO_ALL_AND_SAVE_COPY",
    "SEND_MEETING_INVITATIONS_AND_CANCELLATIONS_CHOICES",
    "SEND_ONLY_TO_CHANGED",
    "SEND_TO_CHANGED_AND_SAVE_COPY",
    "SEND_MEETING_CANCELLATIONS_CHOICES",
    "AFFECTED_TASK_OCCURRENCES_CHOICES",
    "ALL_OCCURRENCES",
    "SPECIFIED_OCCURRENCE_ONLY",
    "CONFLICT_RESOLUTION_CHOICES",
    "NEVER_OVERWRITE",
    "AUTO_RESOLVE",
    "ALWAYS_OVERWRITE",
    "DELETE_TYPE_CHOICES",
    "HARD_DELETE",
    "SOFT_DELETE",
    "MOVE_TO_DELETED_ITEMS",
    "BaseItem",
    "Item",
    "BulkCreateResult",
    "Message",
    "ReplyToItem",
    "ReplyAllToItem",
    "ForwardItem",
    "PostItem",
    "PostReplyItem",
    "Task",
    "ITEM_TRAVERSAL_CHOICES",
    "SHALLOW",
    "SOFT_DELETED",
    "ASSOCIATED",
    "SHAPE_CHOICES",
    "ID_ONLY",
    "DEFAULT",
    "ALL_PROPERTIES",
    "SEARCH_SCOPE_CHOICES",
    "ACTIVE_DIRECTORY",
    "ACTIVE_DIRECTORY_CONTACTS",
    "CONTACTS",
    "CONTACTS_ACTIVE_DIRECTORY",
    "ITEM_CLASSES",
]Sub-modules
- exchangelib.items.base
- exchangelib.items.calendar_item
- exchangelib.items.contact
- exchangelib.items.item
- exchangelib.items.message
- exchangelib.items.post
- exchangelib.items.task
Classes
- class AcceptItem (**kwargs)
- 
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/acceptitem Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class. :param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account. Expand source codeclass AcceptItem(BaseMeetingReplyItem): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/acceptitem""" ELEMENT_NAME = "AcceptItem"AncestorsClass variables- var ELEMENT_NAME
 Inherited members
- class BaseItem (**kwargs)
- 
Base class for all other classes that implement EWS items. Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class. :param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account. Expand source codeclass BaseItem(RegisterMixIn, metaclass=EWSMeta): """Base class for all other classes that implement EWS items.""" ID_ELEMENT_CLS = ItemId _id = IdElementField(field_uri="item:ItemId", value_cls=ID_ELEMENT_CLS) __slots__ = "account", "folder" def __init__(self, **kwargs): """Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class. :param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account. """ from ..account import Account from ..folders import BaseFolder self.account = kwargs.pop("account", None) if self.account is not None and not isinstance(self.account, Account): raise InvalidTypeError("account", self.account, Account) self.folder = kwargs.pop("folder", None) if self.folder is not None: if not isinstance(self.folder, BaseFolder): raise InvalidTypeError("folder", self.folder, BaseFolder) if self.folder.account is not None: if self.account is not None: # Make sure the account from kwargs matches the folder account if self.account != self.folder.account: raise ValueError("'account' does not match 'folder.account'") self.account = self.folder.account super().__init__(**kwargs) @classmethod def from_xml(cls, elem, account): item = super().from_xml(elem=elem, account=account) item.account = account return itemAncestorsSubclassesClass variables- 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 
 Static methods- def from_xml(elem, account)
- 
Expand source code@classmethod def from_xml(cls, elem, account): item = super().from_xml(elem=elem, account=account) item.account = account return item
 Instance variables- var account
- 
Return an attribute of instance, which is of type owner. 
- var folder
- 
Return an attribute of instance, which is of type owner. 
 Inherited members
- class BulkCreateResult (**kwargs)
- 
A dummy class to store return values from a CreateItem service call. Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class. :param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account. Expand source codeclass BulkCreateResult(BaseItem): """A dummy class to store return values from a CreateItem service call.""" attachments = AttachmentField(field_uri="item:Attachments") # ItemAttachment or FileAttachment def __init__(self, **kwargs): super().__init__(**kwargs) if self.attachments is None: self.attachments = []AncestorsClass variables- var FIELDS
 Instance variables- var attachments
 Inherited members
- class CalendarItem (**kwargs)
- 
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/calendaritem Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class. :param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account. Expand source codeclass CalendarItem(Item, AcceptDeclineMixIn): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/calendaritem""" ELEMENT_NAME = "CalendarItem" uid = TextField(field_uri="calendar:UID", is_required_after_save=True, is_searchable=False) recurrence_id = DateTimeField(field_uri="calendar:RecurrenceId", is_read_only=True) start = DateOrDateTimeField(field_uri="calendar:Start", is_required=True) end = DateOrDateTimeField(field_uri="calendar:End", is_required=True) original_start = DateTimeField(field_uri="calendar:OriginalStart", is_read_only=True) is_all_day = BooleanField(field_uri="calendar:IsAllDayEvent", is_required=True, default=False) legacy_free_busy_status = FreeBusyStatusField( field_uri="calendar:LegacyFreeBusyStatus", is_required=True, default="Busy" ) location = TextField(field_uri="calendar:Location") when = TextField(field_uri="calendar:When") is_meeting = BooleanField(field_uri="calendar:IsMeeting", is_read_only=True) is_cancelled = BooleanField(field_uri="calendar:IsCancelled", is_read_only=True) is_recurring = BooleanField(field_uri="calendar:IsRecurring", is_read_only=True) meeting_request_was_sent = BooleanField(field_uri="calendar:MeetingRequestWasSent", is_read_only=True) is_response_requested = BooleanField( field_uri="calendar:IsResponseRequested", default=None, is_required_after_save=True, is_searchable=False ) type = ChoiceField( field_uri="calendar:CalendarItemType", choices={Choice(c) for c in CALENDAR_ITEM_CHOICES}, is_read_only=True ) my_response_type = ChoiceField( field_uri="calendar:MyResponseType", choices={Choice(c) for c in Attendee.RESPONSE_TYPES}, is_read_only=True ) organizer = MailboxField(field_uri="calendar:Organizer", is_read_only=True) required_attendees = AttendeesField(field_uri="calendar:RequiredAttendees", is_searchable=False) optional_attendees = AttendeesField(field_uri="calendar:OptionalAttendees", is_searchable=False) resources = AttendeesField(field_uri="calendar:Resources", is_searchable=False) conflicting_meeting_count = IntegerField(field_uri="calendar:ConflictingMeetingCount", is_read_only=True) adjacent_meeting_count = IntegerField(field_uri="calendar:AdjacentMeetingCount", is_read_only=True) conflicting_meetings = EWSElementListField( field_uri="calendar:ConflictingMeetings", value_cls="CalendarItem", namespace=Item.NAMESPACE, is_read_only=True ) adjacent_meetings = EWSElementListField( field_uri="calendar:AdjacentMeetings", value_cls="CalendarItem", namespace=Item.NAMESPACE, is_read_only=True ) duration = CharField(field_uri="calendar:Duration", is_read_only=True) appointment_reply_time = DateTimeField(field_uri="calendar:AppointmentReplyTime", is_read_only=True) appointment_sequence_number = IntegerField(field_uri="calendar:AppointmentSequenceNumber", is_read_only=True) appointment_state = AppointmentStateField(field_uri="calendar:AppointmentState", is_read_only=True) recurrence = RecurrenceField(field_uri="calendar:Recurrence", is_searchable=False) first_occurrence = OccurrenceField( field_uri="calendar:FirstOccurrence", value_cls=FirstOccurrence, is_read_only=True ) last_occurrence = OccurrenceField(field_uri="calendar:LastOccurrence", value_cls=LastOccurrence, is_read_only=True) modified_occurrences = OccurrenceListField( field_uri="calendar:ModifiedOccurrences", value_cls=Occurrence, is_read_only=True ) deleted_occurrences = OccurrenceListField( field_uri="calendar:DeletedOccurrences", value_cls=DeletedOccurrence, is_read_only=True ) _meeting_timezone = TimeZoneField( field_uri="calendar:MeetingTimeZone", deprecated_from=EXCHANGE_2010, is_searchable=False ) _start_timezone = TimeZoneField( field_uri="calendar:StartTimeZone", supported_from=EXCHANGE_2010, is_searchable=False ) _end_timezone = TimeZoneField(field_uri="calendar:EndTimeZone", supported_from=EXCHANGE_2010, is_searchable=False) conference_type = EnumAsIntField( field_uri="calendar:ConferenceType", enum=CONFERENCE_TYPES, min=0, default=None, is_required_after_save=True ) allow_new_time_proposal = BooleanField( field_uri="calendar:AllowNewTimeProposal", default=None, is_required_after_save=True, is_searchable=False ) is_online_meeting = BooleanField(field_uri="calendar:IsOnlineMeeting", default=None, is_read_only=True) meeting_workspace_url = URIField(field_uri="calendar:MeetingWorkspaceUrl") net_show_url = URIField(field_uri="calendar:NetShowUrl") def occurrence(self, index): """Get an occurrence of a recurring master by index. No query is sent to the server to actually fetch the item. Call refresh() on the item to do so. Only call this method on a recurring master. :param index: The index, which is 1-based :return The occurrence """ return self.__class__( account=self.account, folder=self.folder, _id=OccurrenceItemId(id=self.id, changekey=self.changekey, instance_index=index), ) def recurring_master(self): """Get the recurring master of an occurrence. No query is sent to the server to actually fetch the item. Call refresh() on the item to do so. Only call this method on an occurrence of a recurring master. :return: The master occurrence """ return self.__class__( account=self.account, folder=self.folder, _id=RecurringMasterItemId(id=self.id, changekey=self.changekey), ) @classmethod def timezone_fields(cls): return tuple(f for f in cls.FIELDS if isinstance(f, TimeZoneField)) def clean_timezone_fields(self, version): # Sets proper values on the timezone fields if they are not already set if self.start is None: start_tz = None elif type(self.start) in (EWSDate, datetime.date): start_tz = self.account.default_timezone else: start_tz = self.start.tzinfo if self.end is None: end_tz = None elif type(self.end) in (EWSDate, datetime.date): end_tz = self.account.default_timezone else: end_tz = self.end.tzinfo if version.build < EXCHANGE_2010: if self._meeting_timezone is None: self._meeting_timezone = start_tz self._start_timezone = None self._end_timezone = None else: self._meeting_timezone = None if self._start_timezone is None: self._start_timezone = start_tz if self._end_timezone is None: self._end_timezone = end_tz def clean(self, version=None): super().clean(version=version) if self.start and self.end and self.end < self.start: raise ValueError(f"'end' must be greater than 'start' ({self.start} -> {self.end})") if version: self.clean_timezone_fields(version=version) def cancel(self, **kwargs): return CancelCalendarItem( account=self.account, reference_item_id=ReferenceItemId(id=self.id, changekey=self.changekey), **kwargs ).send() def _update_fieldnames(self): update_fields = super()._update_fieldnames() if self.type == OCCURRENCE: # Some CalendarItem fields cannot be updated when the item is an occurrence. The values are empty when we # receive them so would have been updated because they are set to None. update_fields.remove("recurrence") update_fields.remove("uid") return update_fields @classmethod def from_xml(cls, elem, account): item = super().from_xml(elem=elem, account=account) # EWS returns the start and end values as a datetime regardless of the is_all_day status. Convert to date if # applicable. if not item.is_all_day: return item for field_name in ("start", "end"): val = getattr(item, field_name) if val is None: continue # Return just the date part of the value. Subtract 1 day from the date if this is the end field. This is # the inverse of what we do in .to_xml(). Convert to the local timezone before getting the date. if field_name == "end": val -= datetime.timedelta(days=1) tz = getattr(item, f"_{field_name}_timezone") setattr(item, field_name, val.astimezone(tz).date()) return item def tz_field_for_field_name(self, field_name): meeting_tz_field, start_tz_field, end_tz_field = CalendarItem.timezone_fields() if self.account.version.build < EXCHANGE_2010: return meeting_tz_field if field_name == "start": return start_tz_field if field_name == "end": return end_tz_field raise ValueError("Unsupported field_name") def date_to_datetime(self, field_name): # EWS always expects a datetime. If we have a date value, then convert it to datetime in the local # timezone. Additionally, if this the end field, add 1 day to the date. We could add 12 hours to both # start and end values and let EWS apply its logic, but that seems hacky. value = getattr(self, field_name) tz = getattr(self, self.tz_field_for_field_name(field_name).name) value = EWSDateTime.combine(value, datetime.time(0, 0)).replace(tzinfo=tz) if field_name == "end": value += datetime.timedelta(days=1) return value def to_xml(self, version): # EWS has some special logic related to all-day start and end values. Non-midnight start values are pushed to # the previous midnight. Non-midnight end values are pushed to the following midnight. Midnight in this context # refers to midnight in the local timezone. See # # https://docs.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-create-all-day-events-by-using-ews-in-exchange # elem = super().to_xml(version=version) if not self.is_all_day: return elem for field_name in ("start", "end"): value = getattr(self, field_name) if value is None: continue if type(value) in (EWSDate, datetime.date): # EWS always expects a datetime value = self.date_to_datetime(field_name=field_name) # We already generated an XML element for this field, but it contains a plain date at this point, which # is invalid. Replace the value. field = self.get_field_by_fieldname(field_name) set_xml_value(elem.find(field.response_tag()), value) return elemAncestorsClass variables- var ELEMENT_NAME
- var FIELDS
 Static methods- def from_xml(elem, account)
- 
Expand source code@classmethod def from_xml(cls, elem, account): item = super().from_xml(elem=elem, account=account) # EWS returns the start and end values as a datetime regardless of the is_all_day status. Convert to date if # applicable. if not item.is_all_day: return item for field_name in ("start", "end"): val = getattr(item, field_name) if val is None: continue # Return just the date part of the value. Subtract 1 day from the date if this is the end field. This is # the inverse of what we do in .to_xml(). Convert to the local timezone before getting the date. if field_name == "end": val -= datetime.timedelta(days=1) tz = getattr(item, f"_{field_name}_timezone") setattr(item, field_name, val.astimezone(tz).date()) return item
- def timezone_fields()
- 
Expand source code@classmethod def timezone_fields(cls): return tuple(f for f in cls.FIELDS if isinstance(f, TimeZoneField))
 Instance variables- var adjacent_meeting_count
- var adjacent_meetings
- var allow_new_time_proposal
- var appointment_reply_time
- var appointment_sequence_number
- var appointment_state
- var conference_type
- var conflicting_meeting_count
- var conflicting_meetings
- var deleted_occurrences
- var duration
- var end
- var first_occurrence
- var is_all_day
- var is_cancelled
- var is_meeting
- var is_online_meeting
- var is_recurring
- var is_response_requested
- var last_occurrence
- var legacy_free_busy_status
- var location
- var meeting_request_was_sent
- var meeting_workspace_url
- var modified_occurrences
- var my_response_type
- var net_show_url
- var optional_attendees
- var organizer
- var original_start
- var recurrence
- var recurrence_id
- var required_attendees
- var resources
- var start
- var type
- var uid
- var when
 Methods- def cancel(self, **kwargs)
- 
Expand source codedef cancel(self, **kwargs): return CancelCalendarItem( account=self.account, reference_item_id=ReferenceItemId(id=self.id, changekey=self.changekey), **kwargs ).send()
- def clean(self, version=None)
- 
Expand source codedef clean(self, version=None): super().clean(version=version) if self.start and self.end and self.end < self.start: raise ValueError(f"'end' must be greater than 'start' ({self.start} -> {self.end})") if version: self.clean_timezone_fields(version=version)
- def clean_timezone_fields(self, version)
- 
Expand source codedef clean_timezone_fields(self, version): # Sets proper values on the timezone fields if they are not already set if self.start is None: start_tz = None elif type(self.start) in (EWSDate, datetime.date): start_tz = self.account.default_timezone else: start_tz = self.start.tzinfo if self.end is None: end_tz = None elif type(self.end) in (EWSDate, datetime.date): end_tz = self.account.default_timezone else: end_tz = self.end.tzinfo if version.build < EXCHANGE_2010: if self._meeting_timezone is None: self._meeting_timezone = start_tz self._start_timezone = None self._end_timezone = None else: self._meeting_timezone = None if self._start_timezone is None: self._start_timezone = start_tz if self._end_timezone is None: self._end_timezone = end_tz
- def date_to_datetime(self, field_name)
- 
Expand source codedef date_to_datetime(self, field_name): # EWS always expects a datetime. If we have a date value, then convert it to datetime in the local # timezone. Additionally, if this the end field, add 1 day to the date. We could add 12 hours to both # start and end values and let EWS apply its logic, but that seems hacky. value = getattr(self, field_name) tz = getattr(self, self.tz_field_for_field_name(field_name).name) value = EWSDateTime.combine(value, datetime.time(0, 0)).replace(tzinfo=tz) if field_name == "end": value += datetime.timedelta(days=1) return value
- def occurrence(self, index)
- 
Get an occurrence of a recurring master by index. No query is sent to the server to actually fetch the item. Call refresh() on the item to do so. Only call this method on a recurring master. :param index: The index, which is 1-based :return The occurrence Expand source codedef occurrence(self, index): """Get an occurrence of a recurring master by index. No query is sent to the server to actually fetch the item. Call refresh() on the item to do so. Only call this method on a recurring master. :param index: The index, which is 1-based :return The occurrence """ return self.__class__( account=self.account, folder=self.folder, _id=OccurrenceItemId(id=self.id, changekey=self.changekey, instance_index=index), )
- def recurring_master(self)
- 
Get the recurring master of an occurrence. No query is sent to the server to actually fetch the item. Call refresh() on the item to do so. Only call this method on an occurrence of a recurring master. :return: The master occurrence Expand source codedef recurring_master(self): """Get the recurring master of an occurrence. No query is sent to the server to actually fetch the item. Call refresh() on the item to do so. Only call this method on an occurrence of a recurring master. :return: The master occurrence """ return self.__class__( account=self.account, folder=self.folder, _id=RecurringMasterItemId(id=self.id, changekey=self.changekey), )
- def to_xml(self, version)
- 
Expand source codedef to_xml(self, version): # EWS has some special logic related to all-day start and end values. Non-midnight start values are pushed to # the previous midnight. Non-midnight end values are pushed to the following midnight. Midnight in this context # refers to midnight in the local timezone. See # # https://docs.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-create-all-day-events-by-using-ews-in-exchange # elem = super().to_xml(version=version) if not self.is_all_day: return elem for field_name in ("start", "end"): value = getattr(self, field_name) if value is None: continue if type(value) in (EWSDate, datetime.date): # EWS always expects a datetime value = self.date_to_datetime(field_name=field_name) # We already generated an XML element for this field, but it contains a plain date at this point, which # is invalid. Replace the value. field = self.get_field_by_fieldname(field_name) set_xml_value(elem.find(field.response_tag()), value) return elem
- def tz_field_for_field_name(self, field_name)
- 
Expand source codedef tz_field_for_field_name(self, field_name): meeting_tz_field, start_tz_field, end_tz_field = CalendarItem.timezone_fields() if self.account.version.build < EXCHANGE_2010: return meeting_tz_field if field_name == "start": return start_tz_field if field_name == "end": return end_tz_field raise ValueError("Unsupported field_name")
 Inherited members
- class CancelCalendarItem (**kwargs)
- 
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/cancelcalendaritem Expand source codeclass CancelCalendarItem(BaseReplyItem): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/cancelcalendaritem""" ELEMENT_NAME = "CancelCalendarItem" author_idx = BaseReplyItem.FIELDS.index_by_name("author") FIELDS = BaseReplyItem.FIELDS[:author_idx] + BaseReplyItem.FIELDS[author_idx + 1 :]AncestorsClass variables- var ELEMENT_NAME
- var FIELDS
 Inherited members
- class Contact (**kwargs)
- 
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/contact Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class. :param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account. Expand source codeclass Contact(Item): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/contact""" ELEMENT_NAME = "Contact" file_as = TextField(field_uri="contacts:FileAs") file_as_mapping = ChoiceField( field_uri="contacts:FileAsMapping", choices={ Choice("None"), Choice("LastCommaFirst"), Choice("FirstSpaceLast"), Choice("Company"), Choice("LastCommaFirstCompany"), Choice("CompanyLastFirst"), Choice("LastFirst"), Choice("LastFirstCompany"), Choice("CompanyLastCommaFirst"), Choice("LastFirstSuffix"), Choice("LastSpaceFirstCompany"), Choice("CompanyLastSpaceFirst"), Choice("LastSpaceFirst"), Choice("DisplayName"), Choice("FirstName"), Choice("LastFirstMiddleSuffix"), Choice("LastName"), Choice("Empty"), }, ) display_name = TextField(field_uri="contacts:DisplayName", is_required=True) given_name = CharField(field_uri="contacts:GivenName") initials = TextField(field_uri="contacts:Initials") middle_name = CharField(field_uri="contacts:MiddleName") nickname = TextField(field_uri="contacts:Nickname") complete_name = EWSElementField(field_uri="contacts:CompleteName", value_cls=CompleteName, is_read_only=True) company_name = TextField(field_uri="contacts:CompanyName") email_addresses = EmailAddressesField(field_uri="contacts:EmailAddress") physical_addresses = PhysicalAddressField(field_uri="contacts:PhysicalAddress") phone_numbers = PhoneNumberField(field_uri="contacts:PhoneNumber") assistant_name = TextField(field_uri="contacts:AssistantName") birthday = DateTimeBackedDateField(field_uri="contacts:Birthday", default_time=datetime.time(11, 59)) business_homepage = URIField(field_uri="contacts:BusinessHomePage") children = TextListField(field_uri="contacts:Children") companies = TextListField(field_uri="contacts:Companies", is_searchable=False) contact_source = ChoiceField( field_uri="contacts:ContactSource", choices={Choice("Store"), Choice("ActiveDirectory")}, is_read_only=True ) department = TextField(field_uri="contacts:Department") generation = TextField(field_uri="contacts:Generation") im_addresses = CharField(field_uri="contacts:ImAddresses", is_read_only=True) job_title = TextField(field_uri="contacts:JobTitle") manager = TextField(field_uri="contacts:Manager") mileage = TextField(field_uri="contacts:Mileage") office = TextField(field_uri="contacts:OfficeLocation") postal_address_index = ChoiceField( field_uri="contacts:PostalAddressIndex", choices={Choice("Business"), Choice("Home"), Choice("Other"), Choice("None")}, default="None", is_required_after_save=True, ) profession = TextField(field_uri="contacts:Profession") spouse_name = TextField(field_uri="contacts:SpouseName") surname = CharField(field_uri="contacts:Surname") wedding_anniversary = DateTimeBackedDateField( field_uri="contacts:WeddingAnniversary", default_time=datetime.time(11, 59) ) has_picture = BooleanField(field_uri="contacts:HasPicture", supported_from=EXCHANGE_2010, is_read_only=True) phonetic_full_name = TextField( field_uri="contacts:PhoneticFullName", supported_from=EXCHANGE_2010_SP2, is_read_only=True ) phonetic_first_name = TextField( field_uri="contacts:PhoneticFirstName", supported_from=EXCHANGE_2010_SP2, is_read_only=True ) phonetic_last_name = TextField( field_uri="contacts:PhoneticLastName", supported_from=EXCHANGE_2010_SP2, is_read_only=True ) email_alias = EmailAddressField(field_uri="contacts:Alias", is_read_only=True, supported_from=EXCHANGE_2010_SP2) # 'notes' is documented in MSDN but apparently unused. Writing to it raises ErrorInvalidPropertyRequest. OWA # put entries into the 'notes' form field into the 'body' field. notes = CharField(field_uri="contacts:Notes", supported_from=EXCHANGE_2010_SP2, is_read_only=True) # 'photo' is documented in MSDN but apparently unused. Writing to it raises ErrorInvalidPropertyRequest. OWA # adds photos as FileAttachments on the contact item (with 'is_contact_photo=True'), which automatically flips # the 'has_picture' field. photo = Base64Field(field_uri="contacts:Photo", supported_from=EXCHANGE_2010_SP2, is_read_only=True) user_smime_certificate = Base64Field( field_uri="contacts:UserSMIMECertificate", supported_from=EXCHANGE_2010_SP2, is_read_only=True ) ms_exchange_certificate = Base64Field( field_uri="contacts:MSExchangeCertificate", supported_from=EXCHANGE_2010_SP2, is_read_only=True ) directory_id = TextField(field_uri="contacts:DirectoryId", supported_from=EXCHANGE_2010_SP2, is_read_only=True) manager_mailbox = MailboxField( field_uri="contacts:ManagerMailbox", supported_from=EXCHANGE_2010_SP2, is_read_only=True ) direct_reports = MailboxListField( field_uri="contacts:DirectReports", supported_from=EXCHANGE_2010_SP2, is_read_only=True )AncestorsClass variables- var ELEMENT_NAME
- var FIELDS
 Instance variables- var assistant_name
- var birthday
- var business_homepage
- var children
- var companies
- var company_name
- var complete_name
- var contact_source
- var department
- var direct_reports
- var directory_id
- var display_name
- var email_addresses
- var email_alias
- var file_as
- var file_as_mapping
- var generation
- var given_name
- var has_picture
- var im_addresses
- var initials
- var job_title
- var manager
- var manager_mailbox
- var middle_name
- var mileage
- var ms_exchange_certificate
- var nickname
- var notes
- var office
- var phone_numbers
- var phonetic_first_name
- var phonetic_full_name
- var phonetic_last_name
- var photo
- var physical_addresses
- var postal_address_index
- var profession
- var spouse_name
- var surname
- var user_smime_certificate
- var wedding_anniversary
 Inherited members
- class DeclineItem (**kwargs)
- 
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/declineitem Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class. :param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account. Expand source codeclass DeclineItem(BaseMeetingReplyItem): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/declineitem""" ELEMENT_NAME = "DeclineItem"AncestorsClass variables- var ELEMENT_NAME
 Inherited members
- class DistributionList (**kwargs)
- 
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/distributionlist Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class. :param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account. Expand source codeclass DistributionList(Item): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/distributionlist""" ELEMENT_NAME = "DistributionList" display_name = CharField(field_uri="contacts:DisplayName", is_required=True) file_as = CharField(field_uri="contacts:FileAs", is_read_only=True) contact_source = ChoiceField( field_uri="contacts:ContactSource", choices={Choice("Store"), Choice("ActiveDirectory")}, is_read_only=True ) members = MemberListField(field_uri="distributionlist:Members")AncestorsClass variables- var ELEMENT_NAME
- var FIELDS
 Instance variables- var contact_source
- var display_name
- var file_as
- var members
 Inherited members
- class ForwardItem (**kwargs)
- 
Expand source codeclass ForwardItem(BaseReplyItem): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/forwarditem""" ELEMENT_NAME = "ForwardItem"AncestorsClass variables- var ELEMENT_NAME
 Inherited members
- class Item (**kwargs)
- 
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/item Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class. :param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account. Expand source codeclass Item(BaseItem): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/item""" ELEMENT_NAME = "Item" mime_content = MimeContentField(field_uri="item:MimeContent", is_read_only_after_send=True) _id = BaseItem.FIELDS["_id"] parent_folder_id = EWSElementField(field_uri="item:ParentFolderId", value_cls=ParentFolderId, is_read_only=True) item_class = CharField(field_uri="item:ItemClass", is_read_only=True) subject = CharField(field_uri="item:Subject") sensitivity = ChoiceField( field_uri="item:Sensitivity", choices={Choice("Normal"), Choice("Personal"), Choice("Private"), Choice("Confidential")}, is_required=True, default="Normal", ) text_body = TextField(field_uri="item:TextBody", is_read_only=True, supported_from=EXCHANGE_2013) body = BodyField(field_uri="item:Body") # Accepts and returns Body or HTMLBody instances attachments = AttachmentField(field_uri="item:Attachments") # ItemAttachment or FileAttachment datetime_received = DateTimeField(field_uri="item:DateTimeReceived", is_read_only=True) size = IntegerField(field_uri="item:Size", is_read_only=True) # Item size in bytes categories = CharListField(field_uri="item:Categories") importance = ChoiceField( field_uri="item:Importance", choices={Choice("Low"), Choice("Normal"), Choice("High")}, is_required=True, default="Normal", ) in_reply_to = TextField(field_uri="item:InReplyTo") is_submitted = BooleanField(field_uri="item:IsSubmitted", is_read_only=True) is_draft = BooleanField(field_uri="item:IsDraft", is_read_only=True) is_from_me = BooleanField(field_uri="item:IsFromMe", is_read_only=True) is_resend = BooleanField(field_uri="item:IsResend", is_read_only=True) is_unmodified = BooleanField(field_uri="item:IsUnmodified", is_read_only=True) headers = MessageHeaderField(field_uri="item:InternetMessageHeaders", is_read_only=True) datetime_sent = DateTimeField(field_uri="item:DateTimeSent", is_read_only=True) datetime_created = DateTimeField(field_uri="item:DateTimeCreated", is_read_only=True) response_objects = EWSElementField( field_uri="item:ResponseObjects", value_cls=ResponseObjects, is_read_only=True, ) # Placeholder for ResponseObjects reminder_due_by = DateTimeField(field_uri="item:ReminderDueBy", is_required_after_save=True, is_searchable=False) reminder_is_set = BooleanField(field_uri="item:ReminderIsSet", is_required=True, default=False) reminder_minutes_before_start = IntegerField( field_uri="item:ReminderMinutesBeforeStart", is_required_after_save=True, min=0, default=0 ) display_cc = TextField(field_uri="item:DisplayCc", is_read_only=True) display_to = TextField(field_uri="item:DisplayTo", is_read_only=True) has_attachments = BooleanField(field_uri="item:HasAttachments", is_read_only=True) # ExtendedProperty fields go here culture = CultureField(field_uri="item:Culture", is_required_after_save=True, is_searchable=False) effective_rights = EffectiveRightsField(field_uri="item:EffectiveRights", is_read_only=True) last_modified_name = CharField(field_uri="item:LastModifiedName", is_read_only=True) last_modified_time = DateTimeField(field_uri="item:LastModifiedTime", is_read_only=True) is_associated = BooleanField(field_uri="item:IsAssociated", is_read_only=True, supported_from=EXCHANGE_2010) web_client_read_form_query_string = URIField( field_uri="item:WebClientReadFormQueryString", is_read_only=True, supported_from=EXCHANGE_2010 ) web_client_edit_form_query_string = URIField( field_uri="item:WebClientEditFormQueryString", is_read_only=True, supported_from=EXCHANGE_2010 ) conversation_id = EWSElementField( field_uri="item:ConversationId", value_cls=ConversationId, is_read_only=True, supported_from=EXCHANGE_2010 ) unique_body = BodyField(field_uri="item:UniqueBody", is_read_only=True, supported_from=EXCHANGE_2010) FIELDS = Fields() # Used to register extended properties INSERT_AFTER_FIELD = "has_attachments" def __init__(self, **kwargs): super().__init__(**kwargs) if self.attachments: for a in self.attachments: if a.parent_item: if a.parent_item is not self: raise ValueError(f"'parent_item' of attachment {a} must point to this item") else: a.parent_item = self self.attach(self.attachments) else: self.attachments = [] def save(self, update_fields=None, conflict_resolution=AUTO_RESOLVE, send_meeting_invitations=SEND_TO_NONE): from .task import Task if self.id: item_id, changekey = self._update( update_fieldnames=update_fields, message_disposition=SAVE_ONLY, conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations, ) if ( self.id != item_id and not isinstance(self._id, (OccurrenceItemId, RecurringMasterItemId)) and not isinstance(self, Task) ): # When we update an item with an OccurrenceItemId as ID, EWS returns the ID of the occurrence, so # the ID of this item changes. # # When we update certain fields on a task, the ID may change. A full description is available at # https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/updateitem-operation-task raise ValueError("'id' mismatch in returned update response") # Don't check that changekeys are different. No-op saves will sometimes leave the changekey intact self._id = self.ID_ELEMENT_CLS(item_id, changekey) else: if update_fields: raise ValueError("'update_fields' is only valid for updates") tmp_attachments = None if self.account and self.account.version.build < EXCHANGE_2013 and self.attachments: # At least some versions prior to Exchange 2013 can't save attachments immediately. You need to first # save, then attach. Store the attachment of this item temporarily and attach later. tmp_attachments, self.attachments = self.attachments, [] item = self._create(message_disposition=SAVE_ONLY, send_meeting_invitations=send_meeting_invitations) self._id = self.ID_ELEMENT_CLS(item.id, item.changekey) for old_att, new_att in zip(self.attachments, item.attachments): if old_att.attachment_id is not None: raise ValueError("Old 'attachment_id' is not empty") if new_att.attachment_id is None: raise ValueError("New 'attachment_id' is empty") old_att.attachment_id = new_att.attachment_id if tmp_attachments: # Exchange 2007 workaround. See above self.attach(tmp_attachments) return self @require_account def _create(self, message_disposition, send_meeting_invitations): # Return a BulkCreateResult because we want to return the ID of both the main item *and* attachments. In send # and send-and-save-copy mode, the server does not return an ID, so we just return True. from ..services import CreateItem return CreateItem(account=self.account).get( items=[self], folder=self.folder, message_disposition=message_disposition, send_meeting_invitations=send_meeting_invitations, ) def _update_fieldnames(self): from .contact import Contact, DistributionList # Return the list of fields we are allowed to update update_fieldnames = [] for f in self.supported_fields(version=self.account.version): if f.name == "attachments": # Attachments are handled separately after item creation continue if f.is_read_only: # These cannot be changed continue if (f.is_required or f.is_required_after_save) and ( getattr(self, f.name) is None or (f.is_list and not getattr(self, f.name)) ): # These are required and cannot be deleted continue if not self.is_draft and f.is_read_only_after_send: # These cannot be changed when the item is no longer a draft continue if f.name == "message_id" and f.is_read_only_after_send: # 'message_id' doesn't support updating, no matter the draft status continue if f.name == "mime_content" and isinstance(self, (Contact, DistributionList)): # Contact and DistributionList don't support updating mime_content, no matter the draft status continue update_fieldnames.append(f.name) return update_fieldnames @require_account def _update(self, update_fieldnames, message_disposition, conflict_resolution, send_meeting_invitations): from ..services import UpdateItem if not self.changekey: raise ValueError(f"{self.__class__.__name__} must have changekey") if not update_fieldnames: # The fields to update was not specified explicitly. Update all fields where update is possible update_fieldnames = self._update_fieldnames() return UpdateItem(account=self.account).get( items=[(self, update_fieldnames)], message_disposition=message_disposition, conflict_resolution=conflict_resolution, send_meeting_invitations_or_cancellations=send_meeting_invitations, suppress_read_receipts=True, expect_result=message_disposition != SEND_AND_SAVE_COPY, ) @require_id def refresh(self): # Updates the item based on fresh data from EWS from ..folders import Folder from ..services import GetItem additional_fields = { FieldPath(field=f) for f in Folder(root=self.account.root).allowed_item_fields(version=self.account.version) } res = GetItem(account=self.account).get(items=[self], additional_fields=additional_fields, shape=ID_ONLY) if self.id != res.id and not isinstance(self._id, (OccurrenceItemId, RecurringMasterItemId)): # When we refresh an item with an OccurrenceItemId as ID, EWS returns the ID of the occurrence, so # the ID of this item changes. raise ValueError("'id' mismatch in returned update response") for f in self.FIELDS: setattr(self, f.name, getattr(res, f.name)) # 'parent_item' should point to 'self', not 'fresh_item'. That way, 'fresh_item' can be garbage collected. for a in self.attachments: a.parent_item = self return self @require_id def copy(self, to_folder): from ..services import CopyItem # If 'to_folder' is a public folder or a folder in a different mailbox then None is returned return CopyItem(account=self.account).get( items=[self], to_folder=to_folder, expect_result=None, ) @require_id def move(self, to_folder): from ..services import MoveItem res = MoveItem(account=self.account).get( items=[self], to_folder=to_folder, expect_result=None, ) if res is None: # Assume 'to_folder' is a public folder or a folder in a different mailbox self._id = None return self._id = self.ID_ELEMENT_CLS(*res) self.folder = to_folder def move_to_trash( self, send_meeting_cancellations=SEND_TO_NONE, affected_task_occurrences=ALL_OCCURRENCES, suppress_read_receipts=True, ): # Delete and move to the trash folder. self._delete( delete_type=MOVE_TO_DELETED_ITEMS, send_meeting_cancellations=send_meeting_cancellations, affected_task_occurrences=affected_task_occurrences, suppress_read_receipts=suppress_read_receipts, ) self._id = None self.folder = self.account.trash def soft_delete( self, send_meeting_cancellations=SEND_TO_NONE, affected_task_occurrences=ALL_OCCURRENCES, suppress_read_receipts=True, ): # Delete and move to the dumpster, if it is enabled. self._delete( delete_type=SOFT_DELETE, send_meeting_cancellations=send_meeting_cancellations, affected_task_occurrences=affected_task_occurrences, suppress_read_receipts=suppress_read_receipts, ) self._id = None self.folder = self.account.recoverable_items_deletions def delete( self, send_meeting_cancellations=SEND_TO_NONE, affected_task_occurrences=ALL_OCCURRENCES, suppress_read_receipts=True, ): # Remove the item permanently. No copies are stored anywhere. self._delete( delete_type=HARD_DELETE, send_meeting_cancellations=send_meeting_cancellations, affected_task_occurrences=affected_task_occurrences, suppress_read_receipts=suppress_read_receipts, ) self._id, self.folder = None, None @require_id def _delete(self, delete_type, send_meeting_cancellations, affected_task_occurrences, suppress_read_receipts): from ..services import DeleteItem DeleteItem(account=self.account).get( items=[self], delete_type=delete_type, send_meeting_cancellations=send_meeting_cancellations, affected_task_occurrences=affected_task_occurrences, suppress_read_receipts=suppress_read_receipts, ) @require_id def archive(self, to_folder): from ..services import ArchiveItem return ArchiveItem(account=self.account).get(items=[self], to_folder=to_folder, expect_result=True) def attach(self, attachments): """Add an attachment, or a list of attachments, to this item. If the item has already been saved, the attachments will be created on the server immediately. If the item has not yet been saved, the attachments will be created on the server when the item is saved. Adding attachments to an existing item will update the changekey of the item. :param attachments: """ if not is_iterable(attachments, generators_allowed=True): attachments = [attachments] for a in attachments: if not a.parent_item: a.parent_item = self if self.id and not a.attachment_id: # Already saved object. Attach the attachment server-side now a.attach() if a not in self.attachments: self.attachments.append(a) def detach(self, attachments): """Remove an attachment, or a list of attachments, from this item. If the item has already been saved, the attachments will be deleted on the server immediately. If the item has not yet been saved, the attachments will simply not be created on the server the item is saved. Removing attachments from an existing item will update the changekey of the item. :param attachments: """ if not is_iterable(attachments, generators_allowed=True): attachments = [attachments] if attachments is self.attachments: # Don't remove from the same list we are iterating attachments = list(attachments) for a in attachments: if a.parent_item is not self: raise ValueError("Attachment does not belong to this item") if self.id: # Item is already created. Detach the attachment server-side now a.detach() if a in self.attachments: self.attachments.remove(a) @require_id def create_forward(self, subject, body, to_recipients, cc_recipients=None, bcc_recipients=None): from .message import ForwardItem return ForwardItem( account=self.account, reference_item_id=ReferenceItemId(id=self.id, changekey=self.changekey), subject=subject, new_body=body, to_recipients=to_recipients, cc_recipients=cc_recipients, bcc_recipients=bcc_recipients, ) def forward(self, subject, body, to_recipients, cc_recipients=None, bcc_recipients=None): self.create_forward( subject, body, to_recipients, cc_recipients, bcc_recipients, ).send()AncestorsSubclassesClass variables- var ELEMENT_NAME
- var FIELDS
- var INSERT_AFTER_FIELD
 Instance variables- var attachments
- var body
- var categories
- var conversation_id
- var culture
- var datetime_created
- var datetime_received
- var datetime_sent
- var display_cc
- var display_to
- var effective_rights
- var has_attachments
- var headers
- var importance
- var in_reply_to
- var is_associated
- var is_draft
- var is_from_me
- var is_resend
- var is_submitted
- var is_unmodified
- var item_class
- var last_modified_name
- var last_modified_time
- var mime_content
- var parent_folder_id
- var reminder_due_by
- var reminder_is_set
- var reminder_minutes_before_start
- var response_objects
- var sensitivity
- var size
- var subject
- var text_body
- var unique_body
- var web_client_edit_form_query_string
- var web_client_read_form_query_string
 Methods- def archive(self, to_folder)
- 
Expand source code@require_id def archive(self, to_folder): from ..services import ArchiveItem return ArchiveItem(account=self.account).get(items=[self], to_folder=to_folder, expect_result=True)
- def attach(self, attachments)
- 
Add an attachment, or a list of attachments, to this item. If the item has already been saved, the attachments will be created on the server immediately. If the item has not yet been saved, the attachments will be created on the server when the item is saved. Adding attachments to an existing item will update the changekey of the item. :param attachments: Expand source codedef attach(self, attachments): """Add an attachment, or a list of attachments, to this item. If the item has already been saved, the attachments will be created on the server immediately. If the item has not yet been saved, the attachments will be created on the server when the item is saved. Adding attachments to an existing item will update the changekey of the item. :param attachments: """ if not is_iterable(attachments, generators_allowed=True): attachments = [attachments] for a in attachments: if not a.parent_item: a.parent_item = self if self.id and not a.attachment_id: # Already saved object. Attach the attachment server-side now a.attach() if a not in self.attachments: self.attachments.append(a)
- def copy(self, to_folder)
- 
Expand source code@require_id def copy(self, to_folder): from ..services import CopyItem # If 'to_folder' is a public folder or a folder in a different mailbox then None is returned return CopyItem(account=self.account).get( items=[self], to_folder=to_folder, expect_result=None, )
- def create_forward(self, subject, body, to_recipients, cc_recipients=None, bcc_recipients=None)
- 
Expand source code@require_id def create_forward(self, subject, body, to_recipients, cc_recipients=None, bcc_recipients=None): from .message import ForwardItem return ForwardItem( account=self.account, reference_item_id=ReferenceItemId(id=self.id, changekey=self.changekey), subject=subject, new_body=body, to_recipients=to_recipients, cc_recipients=cc_recipients, bcc_recipients=bcc_recipients, )
- def delete(self, send_meeting_cancellations='SendToNone', affected_task_occurrences='AllOccurrences', suppress_read_receipts=True)
- 
Expand source codedef delete( self, send_meeting_cancellations=SEND_TO_NONE, affected_task_occurrences=ALL_OCCURRENCES, suppress_read_receipts=True, ): # Remove the item permanently. No copies are stored anywhere. self._delete( delete_type=HARD_DELETE, send_meeting_cancellations=send_meeting_cancellations, affected_task_occurrences=affected_task_occurrences, suppress_read_receipts=suppress_read_receipts, ) self._id, self.folder = None, None
- def detach(self, attachments)
- 
Remove an attachment, or a list of attachments, from this item. If the item has already been saved, the attachments will be deleted on the server immediately. If the item has not yet been saved, the attachments will simply not be created on the server the item is saved. Removing attachments from an existing item will update the changekey of the item. :param attachments: Expand source codedef detach(self, attachments): """Remove an attachment, or a list of attachments, from this item. If the item has already been saved, the attachments will be deleted on the server immediately. If the item has not yet been saved, the attachments will simply not be created on the server the item is saved. Removing attachments from an existing item will update the changekey of the item. :param attachments: """ if not is_iterable(attachments, generators_allowed=True): attachments = [attachments] if attachments is self.attachments: # Don't remove from the same list we are iterating attachments = list(attachments) for a in attachments: if a.parent_item is not self: raise ValueError("Attachment does not belong to this item") if self.id: # Item is already created. Detach the attachment server-side now a.detach() if a in self.attachments: self.attachments.remove(a)
- def forward(self, subject, body, to_recipients, cc_recipients=None, bcc_recipients=None)
- 
Expand source codedef forward(self, subject, body, to_recipients, cc_recipients=None, bcc_recipients=None): self.create_forward( subject, body, to_recipients, cc_recipients, bcc_recipients, ).send()
- def move(self, to_folder)
- 
Expand source code@require_id def move(self, to_folder): from ..services import MoveItem res = MoveItem(account=self.account).get( items=[self], to_folder=to_folder, expect_result=None, ) if res is None: # Assume 'to_folder' is a public folder or a folder in a different mailbox self._id = None return self._id = self.ID_ELEMENT_CLS(*res) self.folder = to_folder
- def move_to_trash(self, send_meeting_cancellations='SendToNone', affected_task_occurrences='AllOccurrences', suppress_read_receipts=True)
- 
Expand source codedef move_to_trash( self, send_meeting_cancellations=SEND_TO_NONE, affected_task_occurrences=ALL_OCCURRENCES, suppress_read_receipts=True, ): # Delete and move to the trash folder. self._delete( delete_type=MOVE_TO_DELETED_ITEMS, send_meeting_cancellations=send_meeting_cancellations, affected_task_occurrences=affected_task_occurrences, suppress_read_receipts=suppress_read_receipts, ) self._id = None self.folder = self.account.trash
- def refresh(self)
- 
Expand source code@require_id def refresh(self): # Updates the item based on fresh data from EWS from ..folders import Folder from ..services import GetItem additional_fields = { FieldPath(field=f) for f in Folder(root=self.account.root).allowed_item_fields(version=self.account.version) } res = GetItem(account=self.account).get(items=[self], additional_fields=additional_fields, shape=ID_ONLY) if self.id != res.id and not isinstance(self._id, (OccurrenceItemId, RecurringMasterItemId)): # When we refresh an item with an OccurrenceItemId as ID, EWS returns the ID of the occurrence, so # the ID of this item changes. raise ValueError("'id' mismatch in returned update response") for f in self.FIELDS: setattr(self, f.name, getattr(res, f.name)) # 'parent_item' should point to 'self', not 'fresh_item'. That way, 'fresh_item' can be garbage collected. for a in self.attachments: a.parent_item = self return self
- def save(self, update_fields=None, conflict_resolution='AutoResolve', send_meeting_invitations='SendToNone')
- 
Expand source codedef save(self, update_fields=None, conflict_resolution=AUTO_RESOLVE, send_meeting_invitations=SEND_TO_NONE): from .task import Task if self.id: item_id, changekey = self._update( update_fieldnames=update_fields, message_disposition=SAVE_ONLY, conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations, ) if ( self.id != item_id and not isinstance(self._id, (OccurrenceItemId, RecurringMasterItemId)) and not isinstance(self, Task) ): # When we update an item with an OccurrenceItemId as ID, EWS returns the ID of the occurrence, so # the ID of this item changes. # # When we update certain fields on a task, the ID may change. A full description is available at # https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/updateitem-operation-task raise ValueError("'id' mismatch in returned update response") # Don't check that changekeys are different. No-op saves will sometimes leave the changekey intact self._id = self.ID_ELEMENT_CLS(item_id, changekey) else: if update_fields: raise ValueError("'update_fields' is only valid for updates") tmp_attachments = None if self.account and self.account.version.build < EXCHANGE_2013 and self.attachments: # At least some versions prior to Exchange 2013 can't save attachments immediately. You need to first # save, then attach. Store the attachment of this item temporarily and attach later. tmp_attachments, self.attachments = self.attachments, [] item = self._create(message_disposition=SAVE_ONLY, send_meeting_invitations=send_meeting_invitations) self._id = self.ID_ELEMENT_CLS(item.id, item.changekey) for old_att, new_att in zip(self.attachments, item.attachments): if old_att.attachment_id is not None: raise ValueError("Old 'attachment_id' is not empty") if new_att.attachment_id is None: raise ValueError("New 'attachment_id' is empty") old_att.attachment_id = new_att.attachment_id if tmp_attachments: # Exchange 2007 workaround. See above self.attach(tmp_attachments) return self
- def soft_delete(self, send_meeting_cancellations='SendToNone', affected_task_occurrences='AllOccurrences', suppress_read_receipts=True)
- 
Expand source codedef soft_delete( self, send_meeting_cancellations=SEND_TO_NONE, affected_task_occurrences=ALL_OCCURRENCES, suppress_read_receipts=True, ): # Delete and move to the dumpster, if it is enabled. self._delete( delete_type=SOFT_DELETE, send_meeting_cancellations=send_meeting_cancellations, affected_task_occurrences=affected_task_occurrences, suppress_read_receipts=suppress_read_receipts, ) self._id = None self.folder = self.account.recoverable_items_deletions
 Inherited members
- class MeetingCancellation (**kwargs)
- 
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/meetingcancellation Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class. :param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account. Expand source codeclass MeetingCancellation(BaseMeetingItem): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/meetingcancellation""" ELEMENT_NAME = "MeetingCancellation"AncestorsClass variables- var ELEMENT_NAME
 Inherited members
- class MeetingRequest (**kwargs)
- 
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/meetingrequest Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class. :param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account. Expand source codeclass MeetingRequest(BaseMeetingItem, AcceptDeclineMixIn): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/meetingrequest""" ELEMENT_NAME = "MeetingRequest" meeting_request_type = ChoiceField( field_uri="meetingRequest:MeetingRequestType", choices={ Choice("FullUpdate"), Choice("InformationalUpdate"), Choice("NewMeetingRequest"), Choice("None"), Choice("Outdated"), Choice("PrincipalWantsCopy"), Choice("SilentUpdate"), }, default="None", ) intended_free_busy_status = ChoiceField( field_uri="meetingRequest:IntendedFreeBusyStatus", choices={Choice("Free"), Choice("Tentative"), Choice("Busy"), Choice("OOF"), Choice("NoData")}, is_required=True, default="Busy", ) # This element also has some fields from CalendarItem start_idx = CalendarItem.FIELDS.index_by_name("start") is_response_requested_idx = CalendarItem.FIELDS.index_by_name("is_response_requested") FIELDS = ( BaseMeetingItem.FIELDS + CalendarItem.FIELDS[start_idx:is_response_requested_idx] + CalendarItem.FIELDS[is_response_requested_idx + 1 :] )AncestorsClass variables- var ELEMENT_NAME
- var FIELDS
- var is_response_requested_idx
- var start_idx
 Instance variables- var adjacent_meeting_count
- var adjacent_meetings
- var allow_new_time_proposal
- var appointment_reply_time
- var appointment_sequence_number
- var appointment_state
- var conference_type
- var conflicting_meeting_count
- var conflicting_meetings
- var deleted_occurrences
- var duration
- var end
- var first_occurrence
- var intended_free_busy_status
- var is_all_day
- var is_cancelled
- var is_meeting
- var is_online_meeting
- var is_recurring
- var last_occurrence
- var legacy_free_busy_status
- var location
- var meeting_request_type
- var meeting_request_was_sent
- var meeting_workspace_url
- var modified_occurrences
- var my_response_type
- var net_show_url
- var optional_attendees
- var organizer
- var original_start
- var recurrence
- var required_attendees
- var resources
- var start
- var type
- var when
 Inherited members
- class MeetingResponse (**kwargs)
- 
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/meetingresponse Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class. :param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account. Expand source codeclass MeetingResponse(BaseMeetingItem): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/meetingresponse""" ELEMENT_NAME = "MeetingResponse" proposed_start = DateTimeField(field_uri="meeting:ProposedStart", supported_from=EXCHANGE_2013) proposed_end = DateTimeField(field_uri="meeting:ProposedEnd", supported_from=EXCHANGE_2013)AncestorsClass variables- var ELEMENT_NAME
- var FIELDS
 Instance variables- var proposed_end
- var proposed_start
 Inherited members
- class Message (**kwargs)
- 
Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class. :param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account. Expand source codeclass Message(Item): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/message-ex15websvcsotherref """ ELEMENT_NAME = "Message" sender = MailboxField(field_uri="message:Sender", is_read_only=True, is_read_only_after_send=True) to_recipients = MailboxListField( field_uri="message:ToRecipients", is_read_only_after_send=True, is_searchable=False ) cc_recipients = MailboxListField( field_uri="message:CcRecipients", is_read_only_after_send=True, is_searchable=False ) bcc_recipients = MailboxListField( field_uri="message:BccRecipients", is_read_only_after_send=True, is_searchable=False ) is_read_receipt_requested = BooleanField( field_uri="message:IsReadReceiptRequested", is_required=True, default=False, is_read_only_after_send=True ) is_delivery_receipt_requested = BooleanField( field_uri="message:IsDeliveryReceiptRequested", is_required=True, default=False, is_read_only_after_send=True ) conversation_index = Base64Field(field_uri="message:ConversationIndex", is_read_only=True) conversation_topic = CharField(field_uri="message:ConversationTopic", is_read_only=True) # Rename 'From' to 'author'. We can't use field name 'from' since it's a Python keyword. author = MailboxField(field_uri="message:From", is_read_only_after_send=True) message_id = TextField(field_uri="message:InternetMessageId", is_read_only_after_send=True) is_read = BooleanField(field_uri="message:IsRead", is_required=True, default=False) is_response_requested = BooleanField(field_uri="message:IsResponseRequested", default=False, is_required=True) references = TextField(field_uri="message:References") reply_to = MailboxListField(field_uri="message:ReplyTo", is_read_only_after_send=True, is_searchable=False) received_by = MailboxField(field_uri="message:ReceivedBy", is_read_only=True) received_representing = MailboxField(field_uri="message:ReceivedRepresenting", is_read_only=True) reminder_message_data = EWSElementField( field_uri="message:ReminderMessageData", value_cls=ReminderMessageData, supported_from=EXCHANGE_2013_SP1, is_read_only=True, ) @require_account def send( self, save_copy=True, copy_to_folder=None, conflict_resolution=AUTO_RESOLVE, send_meeting_invitations=SEND_TO_NONE, ): from ..services import SendItem # Only sends a message. The message can either be an existing draft stored in EWS or a new message that does # not yet exist in EWS. if copy_to_folder and not save_copy: raise AttributeError("'save_copy' must be True when 'copy_to_folder' is set") if save_copy and not copy_to_folder: copy_to_folder = self.account.sent # 'Sent' is default EWS behaviour if self.id: SendItem(account=self.account).get(items=[self], saved_item_folder=copy_to_folder) # The item will be deleted from the original folder self._id = None self.folder = copy_to_folder return None # New message if copy_to_folder: # This would better be done via send_and_save() but let's just support it here self.folder = copy_to_folder return self.send_and_save( conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations ) if self.account.version.build < EXCHANGE_2013 and self.attachments: # At least some versions prior to Exchange 2013 can't send attachments immediately. You need to first save, # then attach, then send. This is done in send_and_save(). send() will delete the item again. self.send_and_save( conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations ) return None self._create(message_disposition=SEND_ONLY, send_meeting_invitations=send_meeting_invitations) return None def send_and_save( self, update_fields=None, conflict_resolution=AUTO_RESOLVE, send_meeting_invitations=SEND_TO_NONE ): # Sends Message and saves a copy in the parent folder. Does not return an ItemId. if self.id: self._update( update_fieldnames=update_fields, message_disposition=SEND_AND_SAVE_COPY, conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations, ) else: if self.account.version.build < EXCHANGE_2013 and self.attachments: # At least some versions prior to Exchange 2013 can't send-and-save attachments immediately. You need # to first save, then attach, then send. This is done in save(). self.save( update_fields=update_fields, conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations, ) self.send( save_copy=False, conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations, ) else: self._create(message_disposition=SEND_AND_SAVE_COPY, send_meeting_invitations=send_meeting_invitations) @require_id def create_reply(self, subject, body, to_recipients=None, cc_recipients=None, bcc_recipients=None): if to_recipients is None: if not self.author: raise ValueError("'to_recipients' must be set when message has no 'author'") to_recipients = [self.author] return ReplyToItem( account=self.account, reference_item_id=ReferenceItemId(id=self.id, changekey=self.changekey), subject=subject, new_body=body, to_recipients=to_recipients, cc_recipients=cc_recipients, bcc_recipients=bcc_recipients, ) def reply(self, subject, body, to_recipients=None, cc_recipients=None, bcc_recipients=None): self.create_reply(subject, body, to_recipients, cc_recipients, bcc_recipients).send() @require_id def create_reply_all(self, subject, body): to_recipients = list(self.to_recipients) if self.to_recipients else [] if self.author: to_recipients.append(self.author) return ReplyAllToItem( account=self.account, reference_item_id=ReferenceItemId(id=self.id, changekey=self.changekey), subject=subject, new_body=body, to_recipients=to_recipients, cc_recipients=self.cc_recipients, bcc_recipients=self.bcc_recipients, ) def reply_all(self, subject, body): self.create_reply_all(subject, body).send() def mark_as_junk(self, is_junk=True, move_item=True): """Mark or un-marks items as junk email. :param is_junk: If True, the sender will be added from the blocked sender list. Otherwise, the sender will be removed. :param move_item: If true, the item will be moved to the junk folder. :return: """ from ..services import MarkAsJunk res = MarkAsJunk(account=self.account).get( items=[self], is_junk=is_junk, move_item=move_item, expect_result=None ) if res is None: return self.folder = self.account.junk if is_junk else self.account.inbox self.id, self.changekey = resAncestorsClass variables- var ELEMENT_NAME
- var FIELDS
 Instance variables
- var bcc_recipients
- var cc_recipients
- var conversation_index
- var conversation_topic
- var is_delivery_receipt_requested
- var is_read
- var is_read_receipt_requested
- var is_response_requested
- var message_id
- var received_by
- var received_representing
- var references
- var reminder_message_data
- var reply_to
- var sender
- var to_recipients
 Methods- def create_reply(self, subject, body, to_recipients=None, cc_recipients=None, bcc_recipients=None)
- 
Expand source code@require_id def create_reply(self, subject, body, to_recipients=None, cc_recipients=None, bcc_recipients=None): if to_recipients is None: if not self.author: raise ValueError("'to_recipients' must be set when message has no 'author'") to_recipients = [self.author] return ReplyToItem( account=self.account, reference_item_id=ReferenceItemId(id=self.id, changekey=self.changekey), subject=subject, new_body=body, to_recipients=to_recipients, cc_recipients=cc_recipients, bcc_recipients=bcc_recipients, )
- def create_reply_all(self, subject, body)
- 
Expand source code@require_id def create_reply_all(self, subject, body): to_recipients = list(self.to_recipients) if self.to_recipients else [] if self.author: to_recipients.append(self.author) return ReplyAllToItem( account=self.account, reference_item_id=ReferenceItemId(id=self.id, changekey=self.changekey), subject=subject, new_body=body, to_recipients=to_recipients, cc_recipients=self.cc_recipients, bcc_recipients=self.bcc_recipients, )
- def mark_as_junk(self, is_junk=True, move_item=True)
- 
Mark or un-marks items as junk email. :param is_junk: If True, the sender will be added from the blocked sender list. Otherwise, the sender will be removed. :param move_item: If true, the item will be moved to the junk folder. :return: Expand source codedef mark_as_junk(self, is_junk=True, move_item=True): """Mark or un-marks items as junk email. :param is_junk: If True, the sender will be added from the blocked sender list. Otherwise, the sender will be removed. :param move_item: If true, the item will be moved to the junk folder. :return: """ from ..services import MarkAsJunk res = MarkAsJunk(account=self.account).get( items=[self], is_junk=is_junk, move_item=move_item, expect_result=None ) if res is None: return self.folder = self.account.junk if is_junk else self.account.inbox self.id, self.changekey = res
- def reply(self, subject, body, to_recipients=None, cc_recipients=None, bcc_recipients=None)
- 
Expand source codedef reply(self, subject, body, to_recipients=None, cc_recipients=None, bcc_recipients=None): self.create_reply(subject, body, to_recipients, cc_recipients, bcc_recipients).send()
- def reply_all(self, subject, body)
- 
Expand source codedef reply_all(self, subject, body): self.create_reply_all(subject, body).send()
- def send(self, save_copy=True, copy_to_folder=None, conflict_resolution='AutoResolve', send_meeting_invitations='SendToNone')
- 
Expand source code@require_account def send( self, save_copy=True, copy_to_folder=None, conflict_resolution=AUTO_RESOLVE, send_meeting_invitations=SEND_TO_NONE, ): from ..services import SendItem # Only sends a message. The message can either be an existing draft stored in EWS or a new message that does # not yet exist in EWS. if copy_to_folder and not save_copy: raise AttributeError("'save_copy' must be True when 'copy_to_folder' is set") if save_copy and not copy_to_folder: copy_to_folder = self.account.sent # 'Sent' is default EWS behaviour if self.id: SendItem(account=self.account).get(items=[self], saved_item_folder=copy_to_folder) # The item will be deleted from the original folder self._id = None self.folder = copy_to_folder return None # New message if copy_to_folder: # This would better be done via send_and_save() but let's just support it here self.folder = copy_to_folder return self.send_and_save( conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations ) if self.account.version.build < EXCHANGE_2013 and self.attachments: # At least some versions prior to Exchange 2013 can't send attachments immediately. You need to first save, # then attach, then send. This is done in send_and_save(). send() will delete the item again. self.send_and_save( conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations ) return None self._create(message_disposition=SEND_ONLY, send_meeting_invitations=send_meeting_invitations) return None
- def send_and_save(self, update_fields=None, conflict_resolution='AutoResolve', send_meeting_invitations='SendToNone')
- 
Expand source codedef send_and_save( self, update_fields=None, conflict_resolution=AUTO_RESOLVE, send_meeting_invitations=SEND_TO_NONE ): # Sends Message and saves a copy in the parent folder. Does not return an ItemId. if self.id: self._update( update_fieldnames=update_fields, message_disposition=SEND_AND_SAVE_COPY, conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations, ) else: if self.account.version.build < EXCHANGE_2013 and self.attachments: # At least some versions prior to Exchange 2013 can't send-and-save attachments immediately. You need # to first save, then attach, then send. This is done in save(). self.save( update_fields=update_fields, conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations, ) self.send( save_copy=False, conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations, ) else: self._create(message_disposition=SEND_AND_SAVE_COPY, send_meeting_invitations=send_meeting_invitations)
 Inherited members
- class Persona (**kwargs)
- 
Expand source codeclass Persona(IdChangeKeyMixIn): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/persona""" ELEMENT_NAME = "Persona" ID_ELEMENT_CLS = PersonaId _id = IdElementField(field_uri="persona:PersonaId", value_cls=ID_ELEMENT_CLS, namespace=TNS) persona_type = CharField(field_uri="persona:PersonaType") persona_object_type = TextField(field_uri="persona:PersonaObjectStatus") creation_time = DateTimeField(field_uri="persona:CreationTime") bodies = BodyContentAttributedValueField(field_uri="persona:Bodies") display_name_first_last_sort_key = TextField(field_uri="persona:DisplayNameFirstLastSortKey") display_name_last_first_sort_key = TextField(field_uri="persona:DisplayNameLastFirstSortKey") company_sort_key = TextField(field_uri="persona:CompanyNameSortKey") home_sort_key = TextField(field_uri="persona:HomeCitySortKey") work_city_sort_key = TextField(field_uri="persona:WorkCitySortKey") display_name_first_last_header = CharField(field_uri="persona:DisplayNameFirstLastHeader") display_name_last_first_header = CharField(field_uri="persona:DisplayNameLastFirstHeader") file_as_header = TextField(field_uri="persona:FileAsHeader") display_name = CharField(field_uri="persona:DisplayName") display_name_first_last = CharField(field_uri="persona:DisplayNameFirstLast") display_name_last_first = CharField(field_uri="persona:DisplayNameLastFirst") file_as = CharField(field_uri="persona:FileAs") file_as_id = TextField(field_uri="persona:FileAsId") display_name_prefix = CharField(field_uri="persona:DisplayNamePrefix") given_name = CharField(field_uri="persona:GivenName") middle_name = CharField(field_uri="persona:MiddleName") surname = CharField(field_uri="persona:Surname") generation = CharField(field_uri="persona:Generation") nickname = TextField(field_uri="persona:Nickname") yomi_company_name = TextField(field_uri="persona:YomiCompanyName") yomi_first_name = TextField(field_uri="persona:YomiFirstName") yomi_last_name = TextField(field_uri="persona:YomiLastName") title = CharField(field_uri="persona:Title") department = TextField(field_uri="persona:Department") company_name = CharField(field_uri="persona:CompanyName") email_address = EWSElementField(field_uri="persona:EmailAddress", value_cls=EmailAddress) email_addresses = EWSElementListField(field_uri="persona:EmailAddresses", value_cls=Address) PhoneNumber = PersonaPhoneNumberField(field_uri="persona:PhoneNumber") im_address = CharField(field_uri="persona:ImAddress") home_city = CharField(field_uri="persona:HomeCity") work_city = CharField(field_uri="persona:WorkCity") relevance_score = CharField(field_uri="persona:RelevanceScore") folder_ids = EWSElementListField(field_uri="persona:FolderIds", value_cls=FolderId) attributions = EWSElementListField(field_uri="persona:Attributions", value_cls=Attribution) display_names = StringAttributedValueField(field_uri="persona:DisplayNames") file_ases = StringAttributedValueField(field_uri="persona:FileAses") file_as_ids = StringAttributedValueField(field_uri="persona:FileAsIds") display_name_prefixes = StringAttributedValueField(field_uri="persona:DisplayNamePrefixes") given_names = StringAttributedValueField(field_uri="persona:GivenNames") middle_names = StringAttributedValueField(field_uri="persona:MiddleNames") surnames = StringAttributedValueField(field_uri="persona:Surnames") generations = StringAttributedValueField(field_uri="persona:Generations") nicknames = StringAttributedValueField(field_uri="persona:Nicknames") initials = StringAttributedValueField(field_uri="persona:Initials") yomi_company_names = StringAttributedValueField(field_uri="persona:YomiCompanyNames") yomi_first_names = StringAttributedValueField(field_uri="persona:YomiFirstNames") yomi_last_names = StringAttributedValueField(field_uri="persona:YomiLastNames") business_phone_numbers = PhoneNumberAttributedValueField(field_uri="persona:BusinessPhoneNumbers") business_phone_numbers2 = PhoneNumberAttributedValueField(field_uri="persona:BusinessPhoneNumbers2") home_phones = PhoneNumberAttributedValueField(field_uri="persona:HomePhones") home_phones2 = PhoneNumberAttributedValueField(field_uri="persona:HomePhones2") mobile_phones = PhoneNumberAttributedValueField(field_uri="persona:MobilePhones") mobile_phones2 = PhoneNumberAttributedValueField(field_uri="persona:MobilePhones2") assistant_phone_numbers = PhoneNumberAttributedValueField(field_uri="persona:AssistantPhoneNumbers") callback_phones = PhoneNumberAttributedValueField(field_uri="persona:CallbackPhones") car_phones = PhoneNumberAttributedValueField(field_uri="persona:CarPhones") home_faxes = PhoneNumberAttributedValueField(field_uri="persona:HomeFaxes") organization_main_phones = PhoneNumberAttributedValueField(field_uri="persona:OrganizationMainPhones") other_faxes = PhoneNumberAttributedValueField(field_uri="persona:OtherFaxes") other_telephones = PhoneNumberAttributedValueField(field_uri="persona:OtherTelephones") other_phones2 = PhoneNumberAttributedValueField(field_uri="persona:OtherPhones2") pagers = PhoneNumberAttributedValueField(field_uri="persona:Pagers") radio_phones = PhoneNumberAttributedValueField(field_uri="persona:RadioPhones") telex_numbers = PhoneNumberAttributedValueField(field_uri="persona:TelexNumbers") tty_tdd_phone_numbers = PhoneNumberAttributedValueField(field_uri="persona:TTYTDDPhoneNumbers") work_faxes = PhoneNumberAttributedValueField(field_uri="persona:WorkFaxes") emails1 = EmailAddressAttributedValueField(field_uri="persona:Emails1") emails2 = EmailAddressAttributedValueField(field_uri="persona:Emails2") emails3 = EmailAddressAttributedValueField(field_uri="persona:Emails3") business_home_pages = StringAttributedValueField(field_uri="persona:BusinessHomePages") personal_home_pages = StringAttributedValueField(field_uri="persona:PersonalHomePages") office_locations = StringAttributedValueField(field_uri="persona:OfficeLocations") im_addresses = StringAttributedValueField(field_uri="persona:ImAddresses") im_addresses2 = StringAttributedValueField(field_uri="persona:ImAddresses2") im_addresses3 = StringAttributedValueField(field_uri="persona:ImAddresses3") business_addresses = PostalAddressAttributedValueField(field_uri="persona:BusinessAddresses") home_addresses = PostalAddressAttributedValueField(field_uri="persona:HomeAddresses") other_addresses = PostalAddressAttributedValueField(field_uri="persona:OtherAddresses") titles = StringAttributedValueField(field_uri="persona:Titles") departments = StringAttributedValueField(field_uri="persona:Departments") company_names = StringAttributedValueField(field_uri="persona:CompanyNames") managers = StringAttributedValueField(field_uri="persona:Managers") assistant_names = StringAttributedValueField(field_uri="persona:AssistantNames") professions = StringAttributedValueField(field_uri="persona:Professions") spouse_names = StringAttributedValueField(field_uri="persona:SpouseNames") children = StringAttributedValueField(field_uri="persona:Children") schools = StringAttributedValueField(field_uri="persona:Schools") hobbies = StringAttributedValueField(field_uri="persona:Hobbies") wedding_anniversaries = StringAttributedValueField(field_uri="persona:WeddingAnniversaries") birthdays = StringAttributedValueField(field_uri="persona:Birthdays") locations = StringAttributedValueField(field_uri="persona:Locations") # This class has an additional field of type "ExtendedPropertyAttributedValueField" and # field_uri 'persona:ExtendedProperties'AncestorsClass variables- var ELEMENT_NAME
- var FIELDS
- var ID_ELEMENT_CLS
 Instance variables- var PhoneNumber
- var assistant_names
- var assistant_phone_numbers
- var attributions
- var birthdays
- var bodies
- var business_addresses
- var business_home_pages
- var business_phone_numbers
- var business_phone_numbers2
- var callback_phones
- var car_phones
- var children
- var company_name
- var company_names
- var company_sort_key
- var creation_time
- var department
- var departments
- var display_name
- var display_name_first_last
- var display_name_first_last_header
- var display_name_first_last_sort_key
- var display_name_last_first
- var display_name_last_first_header
- var display_name_last_first_sort_key
- var display_name_prefix
- var display_name_prefixes
- var display_names
- var email_address
- var email_addresses
- var emails1
- var emails2
- var emails3
- var file_as
- var file_as_header
- var file_as_id
- var file_as_ids
- var file_ases
- var folder_ids
- var generation
- var generations
- var given_name
- var given_names
- var hobbies
- var home_addresses
- var home_city
- var home_faxes
- var home_phones
- var home_phones2
- var home_sort_key
- var im_address
- var im_addresses
- var im_addresses2
- var im_addresses3
- var initials
- var locations
- var managers
- var middle_name
- var middle_names
- var mobile_phones
- var mobile_phones2
- var nickname
- var nicknames
- var office_locations
- var organization_main_phones
- var other_addresses
- var other_faxes
- var other_phones2
- var other_telephones
- var pagers
- var persona_object_type
- var persona_type
- var personal_home_pages
- var professions
- var radio_phones
- var relevance_score
- var schools
- var spouse_names
- var surname
- var surnames
- var telex_numbers
- var title
- var titles
- var tty_tdd_phone_numbers
- var wedding_anniversaries
- var work_city
- var work_city_sort_key
- var work_faxes
- var yomi_company_name
- var yomi_company_names
- var yomi_first_name
- var yomi_first_names
- var yomi_last_name
- var yomi_last_names
 Inherited members
- class PostItem (**kwargs)
- 
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/postitem Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class. :param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account. Expand source codeclass PostItem(Item): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/postitem""" ELEMENT_NAME = "PostItem" conversation_index = Message.FIELDS["conversation_index"] conversation_topic = Message.FIELDS["conversation_topic"] author = Message.FIELDS["author"] message_id = Message.FIELDS["message_id"] is_read = Message.FIELDS["is_read"] posted_time = DateTimeField(field_uri="postitem:PostedTime", is_read_only=True) references = TextField(field_uri="message:References") sender = MailboxField(field_uri="message:Sender", is_read_only=True, is_read_only_after_send=True)AncestorsClass variables- var ELEMENT_NAME
- var FIELDS
 Instance variables
- var conversation_index
- var conversation_topic
- var is_read
- var message_id
- var posted_time
- var references
- var sender
 Inherited members
- class PostReplyItem (**kwargs)
- 
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/postreplyitem Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class. :param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account. Expand source codeclass PostReplyItem(Item): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/postreplyitem""" ELEMENT_NAME = "PostReplyItem" # This element only has Item fields up to, and including, 'culture' # TDO: Plus all message fields new_body = BodyField(field_uri="NewBodyContent") # Accepts and returns Body or HTMLBody instances culture_idx = Item.FIELDS.index_by_name("culture") sender_idx = Message.FIELDS.index_by_name("sender") FIELDS = Item.FIELDS[: culture_idx + 1] + Message.FIELDS[sender_idx:]AncestorsClass variables- var ELEMENT_NAME
- var FIELDS
- var culture_idx
- var sender_idx
 Instance variables
- var bcc_recipients
- var cc_recipients
- var conversation_index
- var conversation_topic
- var is_delivery_receipt_requested
- var is_read
- var is_read_receipt_requested
- var is_response_requested
- var message_id
- var new_body
- var received_by
- var received_representing
- var references
- var reminder_message_data
- var reply_to
- var sender
- var to_recipients
 Inherited members
- class RegisterMixIn (**kwargs)
- 
Base class for classes that can change their list of supported fields dynamically. Expand source codeclass RegisterMixIn(IdChangeKeyMixIn, metaclass=EWSMeta): """Base class for classes that can change their list of supported fields dynamically.""" # This class implements dynamic fields on an element class, so we need to include __dict__ in __slots__ __slots__ = ("__dict__",) INSERT_AFTER_FIELD = None @classmethod def register(cls, attr_name, attr_cls): """Register a custom extended property in this item class so they can be accessed just like any other attribute :param attr_name: :param attr_cls: :return: """ if not cls.INSERT_AFTER_FIELD: raise ValueError(f"Class {cls} is missing INSERT_AFTER_FIELD value") try: cls.get_field_by_fieldname(attr_name) except InvalidField: pass else: raise ValueError(f"'attr_name' {attr_name!r} is already registered") if not issubclass(attr_cls, ExtendedProperty): raise TypeError(f"'attr_cls' {attr_cls!r} must be a subclass of type {ExtendedProperty}") # Check if class attributes are properly defined attr_cls.validate_cls() # ExtendedProperty is not a real field, but a placeholder in the fields list. See # https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/item # # Find the correct index for the new extended property, and insert. if attr_cls.is_array_type(): field = ExtendedPropertyListField(attr_name, value_cls=attr_cls) else: field = ExtendedPropertyField(attr_name, value_cls=attr_cls) cls.add_field(field, insert_after=cls.INSERT_AFTER_FIELD) @classmethod def deregister(cls, attr_name): """De-register an extended property that has been registered with register(). :param attr_name: :return: """ try: field = cls.get_field_by_fieldname(attr_name) except InvalidField: raise ValueError(f"{attr_name!r} is not registered") if not isinstance(field, ExtendedPropertyField): raise ValueError(f"{attr_name} is not registered as an ExtendedProperty") cls.remove_field(field)AncestorsSubclassesClass variables- var INSERT_AFTER_FIELD
 Static methods- def deregister(attr_name)
- 
De-register an extended property that has been registered with register(). :param attr_name: :return: Expand source code@classmethod def deregister(cls, attr_name): """De-register an extended property that has been registered with register(). :param attr_name: :return: """ try: field = cls.get_field_by_fieldname(attr_name) except InvalidField: raise ValueError(f"{attr_name!r} is not registered") if not isinstance(field, ExtendedPropertyField): raise ValueError(f"{attr_name} is not registered as an ExtendedProperty") cls.remove_field(field)
- def register(attr_name, attr_cls)
- 
Register a custom extended property in this item class so they can be accessed just like any other attribute :param attr_name: :param attr_cls: :return: Expand source code@classmethod def register(cls, attr_name, attr_cls): """Register a custom extended property in this item class so they can be accessed just like any other attribute :param attr_name: :param attr_cls: :return: """ if not cls.INSERT_AFTER_FIELD: raise ValueError(f"Class {cls} is missing INSERT_AFTER_FIELD value") try: cls.get_field_by_fieldname(attr_name) except InvalidField: pass else: raise ValueError(f"'attr_name' {attr_name!r} is already registered") if not issubclass(attr_cls, ExtendedProperty): raise TypeError(f"'attr_cls' {attr_cls!r} must be a subclass of type {ExtendedProperty}") # Check if class attributes are properly defined attr_cls.validate_cls() # ExtendedProperty is not a real field, but a placeholder in the fields list. See # https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/item # # Find the correct index for the new extended property, and insert. if attr_cls.is_array_type(): field = ExtendedPropertyListField(attr_name, value_cls=attr_cls) else: field = ExtendedPropertyField(attr_name, value_cls=attr_cls) cls.add_field(field, insert_after=cls.INSERT_AFTER_FIELD)
 Inherited members
- class ReplyAllToItem (**kwargs)
- 
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/replyalltoitem Expand source codeclass ReplyAllToItem(BaseReplyItem): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/replyalltoitem""" ELEMENT_NAME = "ReplyAllToItem"AncestorsClass variables- var ELEMENT_NAME
 Inherited members
- class ReplyToItem (**kwargs)
- 
Expand source codeclass ReplyToItem(BaseReplyItem): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/replytoitem""" ELEMENT_NAME = "ReplyToItem"AncestorsClass variables- var ELEMENT_NAME
 Inherited members
- class Task (**kwargs)
- 
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/task Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class. :param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account. Expand source codeclass Task(Item): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/task""" ELEMENT_NAME = "Task" NOT_STARTED = "NotStarted" COMPLETED = "Completed" actual_work = IntegerField(field_uri="task:ActualWork", min=0) assigned_time = DateTimeField(field_uri="task:AssignedTime", is_read_only=True) billing_information = TextField(field_uri="task:BillingInformation") change_count = IntegerField(field_uri="task:ChangeCount", is_read_only=True, min=0) companies = TextListField(field_uri="task:Companies") # 'complete_date' can be set, but is ignored by the server, which sets it to now() complete_date = DateTimeField(field_uri="task:CompleteDate", is_read_only=True) contacts = TextListField(field_uri="task:Contacts") delegation_state = ChoiceField( field_uri="task:DelegationState", choices={ Choice("NoMatch"), Choice("OwnNew"), Choice("Owned"), Choice("Accepted"), Choice("Declined"), Choice("Max"), }, is_read_only=True, ) delegator = CharField(field_uri="task:Delegator", is_read_only=True) due_date = DateTimeBackedDateField(field_uri="task:DueDate") is_editable = BooleanField(field_uri="task:IsAssignmentEditable", is_read_only=True) is_complete = BooleanField(field_uri="task:IsComplete", is_read_only=True) is_recurring = BooleanField(field_uri="task:IsRecurring", is_read_only=True) is_team_task = BooleanField(field_uri="task:IsTeamTask", is_read_only=True) mileage = TextField(field_uri="task:Mileage") owner = CharField(field_uri="task:Owner", is_read_only=True) percent_complete = DecimalField( field_uri="task:PercentComplete", is_required=True, default=Decimal(0.0), min=Decimal(0), max=Decimal(100), is_searchable=False, ) recurrence = TaskRecurrenceField(field_uri="task:Recurrence", is_searchable=False) start_date = DateTimeBackedDateField(field_uri="task:StartDate") status = ChoiceField( field_uri="task:Status", choices={ Choice(NOT_STARTED), Choice("InProgress"), Choice(COMPLETED), Choice("WaitingOnOthers"), Choice("Deferred"), }, is_required=True, is_searchable=False, default=NOT_STARTED, ) status_description = CharField(field_uri="task:StatusDescription", is_read_only=True) total_work = IntegerField(field_uri="task:TotalWork", min=0) def clean(self, version=None): super().clean(version=version) if self.due_date and self.start_date and self.due_date < self.start_date: log.warning( "'due_date' must be greater than 'start_date' (%s vs %s). Resetting 'due_date'", self.due_date, self.start_date, ) self.due_date = self.start_date if self.complete_date: if self.status != self.COMPLETED: log.warning( "'status' must be '%s' when 'complete_date' is set (%s). Resetting", self.COMPLETED, self.status ) self.status = self.COMPLETED now = datetime.datetime.now(tz=UTC) if (self.complete_date - now).total_seconds() > 120: # Reset complete_date values that are in the future # 'complete_date' can be set automatically by the server. Allow some grace between local and server time log.warning("'complete_date' must be in the past (%s vs %s). Resetting", self.complete_date, now) self.complete_date = now if self.start_date and self.complete_date.date() < self.start_date: log.warning( "'complete_date' must be greater than 'start_date' (%s vs %s). Resetting", self.complete_date, self.start_date, ) self.complete_date = EWSDateTime.combine(self.start_date, datetime.time(0, 0)).replace(tzinfo=UTC) if self.percent_complete is not None: if self.status == self.COMPLETED and self.percent_complete != Decimal(100): # percent_complete must be 100% if task is complete log.warning( "'percent_complete' must be 100 when 'status' is '%s' (%s). Resetting", self.COMPLETED, self.percent_complete, ) self.percent_complete = Decimal(100) elif self.status == self.NOT_STARTED and self.percent_complete != Decimal(0): # percent_complete must be 0% if task is not started log.warning( "'percent_complete' must be 0 when 'status' is '%s' (%s). Resetting", self.NOT_STARTED, self.percent_complete, ) self.percent_complete = Decimal(0) def complete(self): # A helper method to mark a task as complete on the server self.status = Task.COMPLETED self.percent_complete = Decimal(100) self.save()AncestorsClass variables- var COMPLETED
- var ELEMENT_NAME
- var FIELDS
- var NOT_STARTED
 Instance variables- var actual_work
- var assigned_time
- var billing_information
- var change_count
- var companies
- var complete_date
- var contacts
- var delegation_state
- var delegator
- var due_date
- var is_complete
- var is_editable
- var is_recurring
- var is_team_task
- var mileage
- var owner
- var percent_complete
- var recurrence
- var start_date
- var status
- var status_description
- var total_work
 Methods- def clean(self, version=None)
- 
Expand source codedef clean(self, version=None): super().clean(version=version) if self.due_date and self.start_date and self.due_date < self.start_date: log.warning( "'due_date' must be greater than 'start_date' (%s vs %s). Resetting 'due_date'", self.due_date, self.start_date, ) self.due_date = self.start_date if self.complete_date: if self.status != self.COMPLETED: log.warning( "'status' must be '%s' when 'complete_date' is set (%s). Resetting", self.COMPLETED, self.status ) self.status = self.COMPLETED now = datetime.datetime.now(tz=UTC) if (self.complete_date - now).total_seconds() > 120: # Reset complete_date values that are in the future # 'complete_date' can be set automatically by the server. Allow some grace between local and server time log.warning("'complete_date' must be in the past (%s vs %s). Resetting", self.complete_date, now) self.complete_date = now if self.start_date and self.complete_date.date() < self.start_date: log.warning( "'complete_date' must be greater than 'start_date' (%s vs %s). Resetting", self.complete_date, self.start_date, ) self.complete_date = EWSDateTime.combine(self.start_date, datetime.time(0, 0)).replace(tzinfo=UTC) if self.percent_complete is not None: if self.status == self.COMPLETED and self.percent_complete != Decimal(100): # percent_complete must be 100% if task is complete log.warning( "'percent_complete' must be 100 when 'status' is '%s' (%s). Resetting", self.COMPLETED, self.percent_complete, ) self.percent_complete = Decimal(100) elif self.status == self.NOT_STARTED and self.percent_complete != Decimal(0): # percent_complete must be 0% if task is not started log.warning( "'percent_complete' must be 0 when 'status' is '%s' (%s). Resetting", self.NOT_STARTED, self.percent_complete, ) self.percent_complete = Decimal(0)
- def complete(self)
- 
Expand source codedef complete(self): # A helper method to mark a task as complete on the server self.status = Task.COMPLETED self.percent_complete = Decimal(100) self.save()
 Inherited members
- class TentativelyAcceptItem (**kwargs)
- 
Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class. :param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account. Expand source codeclass TentativelyAcceptItem(BaseMeetingReplyItem): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/tentativelyacceptitem""" ELEMENT_NAME = "TentativelyAcceptItem"AncestorsClass variables- var ELEMENT_NAME
 Inherited members